I need to make persistent and user specific session counter. I made this
package my.package;
import javax.servlet.http.HttpSessionListener;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSession;
import java.util.HashMap;
public class SessionCounter implements HttpSessionListener {
private static HashMap activeSessions;
public SessionCounter() {
//How to restore session count?
activeSessions = new HashMap();
}
public void sessionCreated(HttpSessionEvent se) {
HttpSession session = se.getSession();
String userName = session.getAttribute("username");
Integer count = (Integer) activeSessions.get(userName);
if (count != null) {
activeSessions.put(userName, Integer.valueOf(count.intValue() + 1));
} else {
activeSessions.put(userName, new Integer(1));
}
}
public void sessionDestroyed(HttpSessionEvent se) {
HttpSession session = se.getSession();
String userName = session.getAttribute("username");
Integer count = (Integer) activeSessions.get(userName);
if (count != null && count.intValue() > 0) {
activeSessions.put(userName, Integer.valueOf(count.intValue() - 1));
}
}
public static HashMap getActiveSessions() {
return activeSessions;
}
}
Session are active even after restart tomcat but session count stored in my activeSessions variable is lost. How can I restore the session count after restart?
When Tomcat is shut down (i.e, by the shutdown script and not by killing the process) all sessions a serialized and restored when it is started the next time.
An HttpSessionListener will always be recreated, therefore your HashMap gets newly instantiated and the information is lost. You will have to write a model that implements serialiable, that holds your data, and store it to the disk when Tomcat is shutdown.
Related
With some help I was able to do asynchronous programming using Completable futures.
However, at the beginning of the application, I am creating a caching and the code is as follows:
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import java.util.concurrent.*;
import org.h2.table.Plan;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class test {
private static final Logger LOGGER = LoggerFactory.getLogger(test.class);
private static LoadingCache<String, Plan> something;
public static void initLoadingCache( ) {
if (something == null) {
something = CacheBuilder.newBuilder()
.concurrencyLevel(10)
.maximumSize(1000) // Maximum of 1000 records can be cached
.expireAfterWrite(60, TimeUnit.MINUTES) // Cache will expire after 60 minutes
.build(new CacheLoader<String, Plan>() { // Build the CacheLoader
#Override
public Plan load(String key) throws Exception{
Plan record = getGraphFromDB(key);
if (record == null) LOGGER.error("DB {} is not present", record);
return record;
}
});
}
}
}
getGraphFromDB is doing nothing but running a correct SQL query.
I observed in my logs I am getting record as null and hence, I threw the exception. Upon further investigation I found that I am getting No session currently bound to execution context as the Error. I added #UnitOfWork in the beginning of the method where I am performing the Async processing using Completable Future (How to perform the transformation on CompletableFuture tasks). The problem still persists and I cannot find out why it is happening.
Update based on the comment:
public Plan getGraphFromDB(String key) throws Exception{
Session session = sessionFactory.openSession();
try {
ManagedSessionContext.bind(session);
Transaction transaction = session.beginTransaction();
try {
return somequery(key);
transaction.commit();
}
catch (Exception e) {
transaction.rollback();
throw new Exception(e.getMessage());
}
} finally {
session.close();
ManagedSessionContext.unbind(sessionFactory);
}
}
I am currently working on a springboot project and I want to execute certain commands whenever a session is created / destroyed. The class SessionListener I created is shown below.
package cn.mypackage.listener;
import java.util.HashSet;
import javax.servlet.ServletContext;
import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
#WebListener
public class SessionListener implements HttpSessionListener {
#Override
public void sessionCreated(HttpSessionEvent event) {
System.out.println("Session Created ---- ");
HttpSession session = event.getSession();
session.setMaxInactiveInterval(10);
System.out.println("Current Session: " + session.getId());
ServletContext application = session.getServletContext();
HashSet<HttpSession> sessions = (HashSet<HttpSession>) application.getAttribute("sessions");
if (sessions == null) {
sessions = new HashSet<HttpSession>();
application.setAttribute("sessions", sessions);
}
sessions.add(session);
System.out.println("Current available sessions: " + Integer.valueOf(sessions.size()).toString());
}
#Override
public void sessionDestroyed(HttpSessionEvent event) throws ClassCastException {
System.out.println("Session Destroyed ---");
HttpSession session = event.getSession();
System.out.println("deletedSessionId: " + session.getId());
System.out.println(session.getCreationTime());
System.out.println(session.getLastAccessedTime());
ServletContext application = session.getServletContext();
HashSet<HttpSession> sessions = (HashSet<HttpSession>) application.getAttribute("sessions");
sessions.remove(session);
}
}
I put this SessionListener.java in my /src/main/java/cn/mypackage/listener/ folder, and my entrance file is in /src/main/java/cn/mypackage/.
However, whenever I opened up a new private browser window and try to access any url, the server side never outputted any information, and breakpoints in both of the functions are useless.
Turns out the problem is that all #RequestMappings I try to access does not pass a parameter of HttpSession. Only when accessing a url with a HttpSession parameter, a session will be created (if not before).
Example:
#Controller
public class APIController {
// this will not generate a session if no session exists, therefore not calling the sessionCreated() function
#RequestMapping("/test1")
public #ResponseBody String test1() {
return "test without session";
}
// this will generate a session if no session exists, therefore calling the sessionCreated() function
#RequestMapping("/test2")
public #ResponseBody String test2(HttpSession session) {
return "test with session";
}
}
I am implementing an upload feature using Grails where basically a user gets to upload a text file and then the system will persist each line of that text file as a database record. While the uploading works fine, larger files take time to process and therefore they ask to have a progress bar so that users can determine if their upload is still processing or an actual error has occurred.
To do this, what I did is to create two URLs:
/upload which is the actual URL that receives the uploaded text file.
/upload/status?uploadToken= which returns the status of a certain upload based on its uploadToken.
What I did is after processing each line, the service will update a session-level counter variable:
// import ...
class UploadService {
Map upload(CommonsMultipartFile record, GrailsParameterMap params) {
Map response = [success: true]
try {
File file = new File(record.getOriginalFilename())
FileUtils.writeByteArrayToFile(file, record.getBytes())
HttpSession session = WebUtils.retrieveGrailsWebRequest().session
List<String> lines = FileUtils.readLines(file, "UTF-8"), errors = []
String uploadToken = params.uploadToken
session.status.put(uploadToken,
[message: "Checking content of the file of errors.",
size: lines.size(),
done: 0])
lines.eachWithIndex { l, li ->
// ... regex checking per line and appending any error to the errors List
session.status.get(uploadToken).done++
}
if(errors.size() == 0) {
session.status.put(uploadToken,
[message: "Persisting record to the database.",
size: lines.size(),
done: 0])
lines.eachWithIndex { l, li ->
// ... Performs GORM manipulation here
session.status.get(uploadToken).done++
}
}
else {
response.success = false
}
}
catch(Exception e) {
response.success = false
}
response << [errors: errors]
return response
}
}
Then create a simple WebSocket implementation that connects to the /upload/status?uploadToken= URL. The problem is that I cannot access the session variable on POGOs. I even change that POGO into a Grails service because I thought that is the cause of the issue, but I still can't access the session variable.
// import ...
#ServerEndpoint("/upload/status")
#WebListener
class UploadEndpointService implements ServletContextListener {
#OnOpen
public void onOpen(Session userSession) { /* ... */ }
#OnClose
public void onClose(Session userSession, CloseReason closeReason) { /* ... */ }
#OnError
public void onError(Throwable t) { /* ... */ }
#OnMessage
public void onMessage(String token, Session userSession) {
// Both of these cause IllegalStateException
def session = WebUtils.retrieveGrailsWebRequest().session
def session = RequestContextHolder.currentRequestAttributes().getSession()
// This returns the session id but I don't know what to do with that information.
String sessionId = userSession.getHttpSessionId()
// Sends the upload status through this line
sendMessage((session.get(token) as JSON).toString(), userSession)
}
private void sendMessage(String message, Session userSession = null) {
Iterator<Session> iterator = users.iterator()
while(iterator.hasNext()) {
iterator.next().basicRemote.sendText(message)
}
}
}
And instead, gives me an error:
Caused by IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case,
use RequestContextListener or RequestContextFilter to expose the current request.
I already verified that the web socket is working by making it send a static String content. But what I want is to be able to get that counter and set it as the send message. I'm using Grails 2.4.4 and the Grails Spring Websocket plugin, while looks promising, is only available from Grails 3 onwards. Is there any way to achieve this, or if not, what approach should I use?
Much thanks to the answer to this question that helped me greatly solving my problem.
I just modified my UploadEndpointService the same as the one on that answer and instead of making it as a service class, I reverted it back into a POGO. I also configured it's #Serverendpoint annotation and added a configurator value. I also added a second parameter to the onOpen() method. Here is the edited class:
import grails.converters.JSON
import grails.util.Environment
import javax.servlet.annotation.WebListener
import javax.servlet.http.HttpSession
import javax.servlet.ServletContext
import javax.servlet.ServletContextEvent
import javax.servlet.ServletContextListener
import javax.websocket.CloseReason
import javax.websocket.EndpointConfig
import javax.websocket.OnClose
import javax.websocket.OnError
import javax.websocket.OnMessage
import javax.websocket.OnOpen
import javax.websocket.server.ServerContainer
import javax.websocket.server.ServerEndpoint
import javax.websocket.Session
import org.apache.log4j.Logger
import org.codehaus.groovy.grails.commons.GrailsApplication
import org.codehaus.groovy.grails.web.json.JSONObject
import org.codehaus.groovy.grails.web.servlet.GrailsApplicationAttributes
import org.springframework.context.ApplicationContext
#ServerEndpoint(value="/ep/maintenance/attendance-monitoring/upload/status", configurator=GetHttpSessionConfigurator.class)
#WebListener
class UploadEndpoint implements ServletContextListener {
private static final Logger log = Logger.getLogger(UploadEndpoint.class)
private Session wsSession
private HttpSession httpSession
#Override
void contextInitialized(ServletContextEvent servletContextEvent) {
ServletContext servletContext = servletContextEvent.servletContext
ServerContainer serverContainer = servletContext.getAttribute("javax.websocket.server.ServerContainer")
try {
if (Environment.current == Environment.DEVELOPMENT) {
serverContainer.addEndpoint(UploadEndpoint)
}
ApplicationContext ctx = (ApplicationContext) servletContext.getAttribute(GrailsApplicationAttributes.APPLICATION_CONTEXT)
GrailsApplication grailsApplication = ctx.grailsApplication
serverContainer.defaultMaxSessionIdleTimeout = grailsApplication.config.servlet.defaultMaxSessionIdleTimeout ?: 0
} catch (IOException e) {
log.error(e.message, e)
}
}
#Override
void contextDestroyed(ServletContextEvent servletContextEvent) {
}
#OnOpen
public void onOpen(Session userSession, EndpointConfig config) {
this.wsSession = userSession
this.httpSession = (HttpSession) config.getUserProperties().get(HttpSession.class.getName())
}
#OnMessage
public void onMessage(String message, Session userSession) {
try {
Map params = new JSONObject(message)
if(httpSession.status == null) {
params = [message: "Initializing file upload.",
size: 0,
token: 0]
sendMessage((params as JSON).toString())
}
else {
sendMessage((httpSession.status.get(params.token) as JSON).toString())
}
}
catch(IllegalStateException e) {
}
}
#OnClose
public void onClose(Session userSession, CloseReason closeReason) {
try {
userSession.close()
}
catch(IllegalStateException e) {
}
}
#OnError
public void onError(Throwable t) {
log.error(t.message, t)
}
private void sendMessage(String message, Session userSession=null) {
wsSession.basicRemote.sendText(message)
}
}
The real magic happens within the onOpen() method. There is where the accessing of the session variable takes place.
I've read Java EE documentation and for me is unclear one thing. According to API, the only way to find another Session is a code like this: (assuming that we've identifier of other session):
import javax.websocket.Session;
...
private static Session findOtherSessionById(Session user, String id) {
for (Session session : user.getOpenSessions()) {
if (id.equals(session.getId())) {
return session;
}
}
return null;
}
But when we've thousands of users, this code is a performance bottleneck.
So, is there a way to get Session by id fast without using own ConcurrentHashMap for this? Or maybe some application server has undocummented feature for this (for me Wildfly would be great)?
You can do something like:
Map<String, Session> map = new HashMap<>();
static Map<String, Session> peers = Collections.synchronizedMap(map);
#OnOpen
public void onOpen(Session session) {
peers.add(session.getId(), session);
}
#OnClose
public void onClose(Session session) {
peers.remove(session.getId());
}
private static Session findOtherSessionById(Session user, String id) {
if (peers.containsKey(user.getId()) {
return peers.get(user.getId());
}
}
I have one servlet taking care of multiple sites and therefore I want to have different sessions for different sites, even if its the same user.
Is there any support for this in Java or do I need to prefix the attribute names instead? I guess prefixing is not a good idea.
/Br Johannes
This CANNOT be done in the servlet container based on URL parameters alone; you'll have to do it yourself. Instead of dealing with attribute prefixes in your servlet, however, the easiest way to manage "separate" sessions is via filter:
Write a simple wrapper class for HttpSession. Have it hold a Map of attributes and back all attribute / value methods by said map; delegate all the other methods to the actual session you're wrapping. Override invalidate() method to remove your session wrapper instead of killing the entire "real" session.
Write a servlet filter; map it to intercept all applicable URLs.
Maintain a collection of your session wrappers as an attribute within the real session.
In your filter's doFilter() method extract the appropriate session wrapper from the collection and inject it into the request you're passing down the chain by wrapping the original request into HttpServletRequestWrapper whose getSession() method is overwritten.
Your servlets / JSPs / etc... will enjoy "separate" sessions.
Note that Sessions's "lastAccessedTime" is shared with this approach. If you need to keep those separate you'll have to write your own code for maintaining this setting and for expiring your session wrappers.
I recently came across this problem too, and I went with ChssPly76's suggestion to solve it. I thought I'd post my results here to provide a reference implementation. It hasn't been extensively tested, so kindly let me know if you spot any weaknesses.
I assume that every request to a servlet contains a parameter named uiid, which represents a user ID. The requester has to keep track of sending a new ID everytime a link is clicked that opens a new window. In my case this is sufficient, but feel free to use any other (maybe more secure) method here. Furthermore, I work with Tomcat 7 or 8. You might need to extend other classes when working with different servlet containers, but the APIs shouldn't change too much.
In the following, the created sessions are referred to as subsessions, the original container managed session is the parent session. The implementation consists of the following five classes:
The SingleSessionManager keeps track of creation, distribution and cleanup of all subsessions. It does this by acting as a servlet filter which replaces the ServletRequest with a wrapper that returns the appropriate subsession. A scheduler periodically checks for expired subsessions ...and yes, it's a singleton. Sorry, but I still like them.
package session;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
/**
* A singleton class that manages multiple sessions on top of a regular container managed session.
* See web.xml for information on how to enable this.
*
*/
public class SingleSessionManager implements Filter {
/**
* The default session timeout in seconds to be used if no explicit timeout is provided.
*/
public static final int DEFAULT_TIMEOUT = 900;
/**
* The default interval for session validation checks in seconds to be used if no explicit
* timeout is provided.
*/
public static final int DEFAULT_SESSION_INVALIDATION_CHECK = 15;
private static SingleSessionManager instance;
private ScheduledExecutorService scheduler;
protected int timeout;
protected long sessionInvalidationCheck;
private Map<SubSessionKey, HttpSessionWrapper> sessions = new ConcurrentHashMap<SubSessionKey, HttpSessionWrapper>();
public SingleSessionManager() {
sessionInvalidationCheck = DEFAULT_SESSION_INVALIDATION_CHECK;
timeout = DEFAULT_TIMEOUT;
}
public static SingleSessionManager getInstance() {
if (instance == null) {
instance = new SingleSessionManager();
}
return instance;
}
#Override
public void destroy() {
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequestWrapper wrapper = new HttpServletRequestWrapper((HttpServletRequest) request);
chain.doFilter(wrapper, response);
}
#Override
public void init(FilterConfig cfg) throws ServletException {
String timeout = cfg.getInitParameter("sessionTimeout");
if (timeout != null && !timeout.trim().equals("")) {
getInstance().timeout = Integer.parseInt(timeout) * 60;
}
String sessionInvalidationCheck = cfg.getInitParameter("sessionInvalidationCheck");
if (sessionInvalidationCheck != null && !sessionInvalidationCheck.trim().equals("")) {
getInstance().sessionInvalidationCheck = Long.parseLong(sessionInvalidationCheck);
}
getInstance().startSessionExpirationScheduler();
}
/**
* Create a new session ID.
*
* #return A new unique session ID.
*/
public String generateSessionId() {
return UUID.randomUUID().toString();
}
protected void startSessionExpirationScheduler() {
if (scheduler == null) {
scheduler = Executors.newScheduledThreadPool(1);
final Runnable sessionInvalidator = new Runnable() {
public void run() {
SingleSessionManager.getInstance().destroyExpiredSessions();
}
};
final ScheduledFuture<?> sessionInvalidatorHandle =
scheduler.scheduleAtFixedRate(sessionInvalidator
, this.sessionInvalidationCheck
, this.sessionInvalidationCheck
, TimeUnit.SECONDS);
}
}
/**
* Get the timeout after which a session will be invalidated.
*
* #return The timeout of a session in seconds.
*/
public int getSessionTimeout() {
return timeout;
}
/**
* Retrieve a session.
*
* #param uiid
* The user id this session is to be associated with.
* #param create
* If <code>true</code> and no session exists for the given user id, a new session is
* created and associated with the given user id. If <code>false</code> and no
* session exists for the given user id, no new session will be created and this
* method will return <code>null</code>.
* #param originalSession
* The original backing session created and managed by the servlet container.
* #return The session associated with the given user id if this session exists and/or create is
* set to <code>true</code>, <code>null</code> otherwise.
*/
public HttpSession getSession(String uiid, boolean create, HttpSession originalSession) {
if (uiid != null) {
SubSessionKey key = new SubSessionKey(originalSession.getId(), uiid);
if (!sessions.containsKey(key) && create) {
HttpSessionWrapper sw = new HttpSessionWrapper(uiid, originalSession);
sessions.put(key, sw);
}
HttpSessionWrapper session = sessions.get(key);
session.setLastAccessedTime(System.currentTimeMillis());
return session;
}
return null;
}
public HttpSessionWrapper removeSession(SubSessionKey key) {
return sessions.remove(key);
}
/**
* Destroy a session, freeing all it's resources.
*
* #param session
* The session to be destroyed.
*/
public void destroySession(HttpSessionWrapper session) {
String uiid = ((HttpSessionWrapper)session).getUiid();
SubSessionKey key = new SubSessionKey(session.getOriginalSession().getId(), uiid);
HttpSessionWrapper w = getInstance().removeSession(key);
if (w != null) {
System.out.println("Session " + w.getId() + " with uiid " + uiid + " was destroyed.");
} else {
System.out.println("uiid " + uiid + " does not have a session.");
}
}
/**
* Destroy all session that are expired at the time of this method call.
*/
public void destroyExpiredSessions() {
List<HttpSessionWrapper> markedForDelete = new ArrayList<HttpSessionWrapper>();
long time = System.currentTimeMillis() / 1000;
for (HttpSessionWrapper session : sessions.values()) {
if (time - (session.getLastAccessedTime() / 1000) >= session.getMaxInactiveInterval()) {
markedForDelete.add(session);
}
}
for (HttpSessionWrapper session : markedForDelete) {
destroySession(session);
}
}
/**
* Remove all subsessions that were created from a given parent session.
*
* #param originalSession
* All subsessions created with this session as their parent session will be
* invalidated.
*/
public void clearAllSessions(HttpSession originalSession) {
Iterator<HttpSessionWrapper> it = sessions.values().iterator();
while (it.hasNext()) {
HttpSessionWrapper w = it.next();
if (w.getOriginalSession().getId().equals(originalSession.getId())) {
destroySession(w);
}
}
}
public void setSessionTimeout(int timeout) {
this.timeout = timeout;
}
}
A subsession is identified by a SubSessionKey. These key objects depend on the uiid and the ID of the parent session.
package session;
/**
* Key object for identifying a subsession.
*
*/
public class SubSessionKey {
private String sessionId;
private String uiid;
/**
* Create a new instance of {#link SubSessionKey}.
*
* #param sessionId
* The session id of the parent session.
* #param uiid
* The users's id this session is associated with.
*/
public SubSessionKey(String sessionId, String uiid) {
super();
this.sessionId = sessionId;
this.uiid = uiid;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((sessionId == null) ? 0 : sessionId.hashCode());
result = prime * result + ((uiid == null) ? 0 : uiid.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
SubSessionKey other = (SubSessionKey) obj;
if (sessionId == null) {
if (other.sessionId != null)
return false;
} else if (!sessionId.equals(other.sessionId))
return false;
if (uiid == null) {
if (other.uiid != null)
return false;
} else if (!uiid.equals(other.uiid))
return false;
return true;
}
#Override
public String toString() {
return "SubSessionKey [sessionId=" + sessionId + ", uiid=" + uiid + "]";
}
}
The HttpServletRequestWrapper wraps a HttpServletRequest object. All methods are redirected to the wrapped request except for the getSession methods which will return an HttpSessionWrapper depending on the user ID in this request's parameters.
package session;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
/**
* Wrapper class that wraps a {#link HttpServletRequest} object. All methods are redirected to the
* wrapped request except for the <code>getSession</code> which will return an
* {#link HttpSessionWrapper} depending on the user id in this request's parameters.
*
*/
public class HttpServletRequestWrapper extends javax.servlet.http.HttpServletRequestWrapper {
private HttpServletRequest req;
public HttpServletRequestWrapper(HttpServletRequest req) {
super(req);
this.req = req;
}
#Override
public HttpSession getSession() {
return getSession(true);
}
#Override
public HttpSession getSession(boolean create) {
String[] uiid = getParameterMap().get("uiid");
if (uiid != null && uiid.length >= 1) {
return SingleSessionManager.getInstance().getSession(uiid[0], create, req.getSession(create));
}
return req.getSession(create);
}
}
The HttpSessionWrapper represents a subsession.
package session;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionContext;
/**
* Implementation of a HttpSession. Each instance of this class is created around a container
* managed parent session with it's lifetime linked to it's parent's.
*
*/
#SuppressWarnings("deprecation")
public class HttpSessionWrapper implements HttpSession {
private Map<String, Object> attributes;
private Map<String, Object> values;
private long creationTime;
private String id;
private String uiid;
private boolean isNew;
private long lastAccessedTime;
private HttpSession originalSession;
public HttpSessionWrapper(String uiid, HttpSession originalSession) {
creationTime = System.currentTimeMillis();
lastAccessedTime = creationTime;
id = SingleSessionManager.getInstance().generateSessionId();
isNew = true;
attributes = new HashMap<String, Object>();
Enumeration<String> names = originalSession.getAttributeNames();
while (names.hasMoreElements()) {
String name = names.nextElement();
attributes.put(name, originalSession.getAttribute(name));
}
values = new HashMap<String, Object>();
for (String name : originalSession.getValueNames()) {
values.put(name, originalSession.getValue(name));
}
this.uiid = uiid;
this.originalSession = originalSession;
}
public String getUiid() {
return uiid;
}
public void setNew(boolean b) {
isNew = b;
}
public void setLastAccessedTime(long time) {
lastAccessedTime = time;
}
#Override
public Object getAttribute(String arg0) {
return attributes.get(arg0);
}
#Override
public Enumeration<String> getAttributeNames() {
return Collections.enumeration(attributes.keySet());
}
#Override
public long getCreationTime() {
return creationTime;
}
#Override
public String getId() {
return id;
}
#Override
public long getLastAccessedTime() {
return lastAccessedTime;
}
#Override
public int getMaxInactiveInterval() {
return SingleSessionManager.getInstance().getSessionTimeout();
}
#Override
public ServletContext getServletContext() {
return originalSession.getServletContext();
}
#Override
public HttpSessionContext getSessionContext() {
return new HttpSessionContext() {
#Override
public Enumeration<String> getIds() {
return Collections.enumeration(new HashSet<String>());
}
#Override
public HttpSession getSession(String arg0) {
return null;
}
};
}
#Override
public Object getValue(String arg0) {
return values.get(arg0);
}
#Override
public String[] getValueNames() {
return values.keySet().toArray(new String[values.size()]);
}
#Override
public void invalidate() {
SingleSessionManager.getInstance().destroySession(this);
}
#Override
public boolean isNew() {
return isNew;
}
#Override
public void putValue(String arg0, Object arg1) {
values.put(arg0, arg1);
}
#Override
public void removeAttribute(String arg0) {
attributes.remove(arg0);
}
#Override
public void removeValue(String arg0) {
values.remove(arg0);
}
#Override
public void setAttribute(String arg0, Object arg1) {
attributes.put(arg0, arg1);
}
#Override
public void setMaxInactiveInterval(int arg0) {
SingleSessionManager.getInstance().setSessionTimeout(arg0);
}
public HttpSession getOriginalSession() {
return originalSession;
}
}
The SessionInvalidator is an HttpSessionListener that takes care of cleaning all subsessions in case of the invalidation of their parent session.
package session;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
/**
* Session listener that listens for the destruction of a container managed session and takes care
* of destroying all it's subsessions.
* <p>
* Normally this listener won't have much to do since subsessions usually have a shorter lifetime
* than their parent session and therefore will timeout long before this method is called. This
* listener will only be important in case of an explicit invalidation of a parent session.
* </p>
*
*/
public class SessionInvalidator implements HttpSessionListener {
#Override
public void sessionCreated(HttpSessionEvent arg0) {
}
#Override
public void sessionDestroyed(HttpSessionEvent arg0) {
SingleSessionManager.getInstance().clearAllSessions(arg0.getSession());
}
}
Enable everything by putting the following in your web.xml
<filter>
<filter-name>SingleSessionFilter</filter-name>
<filter-class>de.supportgis.sgjWeb.session.SingleSessionManager</filter-class>
<!-- The timeout in minutes after which a subsession will be invalidated. It is recommended to set a session timeout for the servled container using the parameter "session-timeout", which is higher than this value. -->
<init-param>
<param-name>sessionTimeout</param-name>
<param-value>1</param-value>
</init-param>
<init-param>
<!-- The intervall in seconds in which a check for expired sessions will be performed. -->
<param-name>sessionInvalidationCheck</param-name>
<param-value>15</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>SingleSessionFilter</filter-name>
<!-- Insert the name of your servlet here to which the session management should apply, or use url-pattern instead. -->
<servlet-name>YourServlet</servlet-name>
</filter-mapping>
<listener>
<listener-class>session.SessionInvalidator</listener-class>
</listener>
<!-- Timeout of the parent session -->
<session-config>
<session-timeout>40</session-timeout>
<!-- Session timeout interval in minutes -->
</session-config>
I think you're looking for something like Apache Tomcat. It will manage individual sessions for individual servlet applications.
The session is unique for a combination of user and web application. You can of course deploy your servlet in several web applications on the same Tomcat instance, but you will not be able to route the HTTP request to different web applications simply based on URL parameters unless you evaluate the URL parameters in a second servlet and redirect the browser to a new URL for the specific web app.
Different servlet containers or J2EE app servers may have different options for routing requests to specific web applications, but AFAIK out of the box, Tomcat can only delegate the request based on either host name or base directory, e.g.:
http://app1/... or http://server/app1/... is delegated to app1
http://app2/... or http://server/app2/... is delegated to app2, and so on
here is a bug fix for user3792852's reply
public HttpSession getSession(String uiid, boolean create, HttpSession originalSession)
{
if (uiid != null && originalSession != null)
{
SubSessionKey key = new SubSessionKey(originalSession.getId(), uiid);
synchronized (sessions)
{
HttpSessionWrapper session = sessions.get(key);
if (session == null && create)
{
session = new HttpSessionWrapper(uiid, originalSession);
sessions.put(key, session);
}
if (session != null)
{
session.setLastAccessedTime(System.currentTimeMillis());
}
return session;
}
}
return null;
}