/*
* JBoss, Home of Professional Open Source
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package org.jboss.seam.wiki.admin;
import org.jboss.seam.ScopeType;
import org.jboss.seam.wiki.core.model.User;
import org.jboss.seam.wiki.core.model.Role;
import org.jboss.seam.wiki.core.dao.UserDAO;
import org.jboss.seam.wiki.util.WikiUtil;
import org.jboss.seam.annotations.*;
import org.jboss.seam.annotations.security.Restrict;
import org.jboss.seam.log.Log;
import org.jboss.seam.security.Identity;
import javax.servlet.http.HttpSession;
import javax.persistence.EntityManager;
import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.*;
/**
* @author Christian Bauer
*/
@Name("wikiHttpSessionManager")
@Scope(ScopeType.CONVERSATION)
@AutoCreate
public class WikiHttpSessionManager implements Serializable {
protected static final String SESSION_ATTR_IDENTITY = "org.jboss.seam.security.identity";
protected static final String SESSION_ATTR_ACCESSLVL = "currentAccessLevel";
protected static final String SESSION_ATTR_USER = "currentUser";
@Logger
private Log log;
@In
UserDAO userDAO;
transient private Map<String, Boolean> selectedSessions = new HashMap<String,Boolean>();
transient private Map<String, Long> sessionsSize = new HashMap<String,Long>();
transient private List<OnlineUser> onlineMembers;
@Restrict("#{s:hasPermission('User', 'isAdmin', currentUser)}")
public Map<String, Boolean> getSelectedSessions() { return selectedSessions; }
@Restrict("#{s:hasPermission('User', 'isAdmin', currentUser)}")
public Map<String, Long> getSessionsSize() { return sessionsSize; }
@Restrict("#{s:hasPermission('User', 'isAdmin', currentUser)}")
public List<HttpSession> getSessions() {
return new ArrayList(WikiServletListener.getSessions().values());
}
@Restrict("#{s:hasPermission('User', 'isAdmin', currentUser)}")
public HttpSession getSession(String id) {
return WikiServletListener.getSessions().get(id);
}
/**
* Calculate the size of an HttpSession using serialization.
* <p>
* This is extremely crude and a guesstimate, especially because this ignores any
* serialization errors.
* </p>
*
* @param id the identifier of th HttpSession
* @return size in bytes
*/
@Restrict("#{s:hasPermission('User', 'isAdmin', currentUser)}")
public long getSessionSize(String id) {
HttpSession session = WikiServletListener.getSessions().get(id);
long sessionSize = 0;
if (session != null) {
Enumeration elem = session.getAttributeNames();
while (elem.hasMoreElements()) {
String attName = (String)elem.nextElement();
log.debug("serializing session attribute: " + attName);
ByteArrayOutputStream bos = null;
try {
bos = new ByteArrayOutputStream();
ObjectOutputStream out = new ObjectOutputStream(bos);
out.writeObject(
session.getAttribute(attName)
);
out.close();
} catch (Exception ex) {
// Just swallow that
log.warn("error during serialization, ignoring: " + ex);
}
if (bos != null) {
byte[] buf = bos.toByteArray();
sessionSize = sessionSize + buf.length;
}
}
}
return sessionSize;
}
@Restrict("#{s:hasPermission('User', 'isAdmin', currentUser)}")
public String getUsername(String id) {
log.debug("trying to get username of Http session: " + id);
HttpSession session = WikiServletListener.getSessions().get(id);
String username = User.GUEST_USERNAME;
if (session != null) {
Identity identity = (Identity)session.getAttribute(SESSION_ATTR_IDENTITY);
if (identity != null && identity.getPrincipal() != null)
username = identity.getPrincipal().getName();
}
return username;
}
@Restrict("#{s:hasPermission('User', 'isAdmin', currentUser)}")
public void calculateSelectedSessionsSize() {
sessionsSize.clear();
for (Map.Entry<String, Boolean> entry : selectedSessions.entrySet()) {
if (entry.getValue()) {
log.debug("calculating size of Http session: " + entry.getKey());
sessionsSize.put(
entry.getKey(),
getSessionSize( entry.getKey() )
);
}
}
selectedSessions.clear();
}
@Restrict("#{s:hasPermission('User', 'isAdmin', currentUser)}")
public void refresh() {
selectedSessions.clear();
}
/* TODO: The way Seam handles sessions conflicts with "destroying" it from the "outside"
@Restrict("#{s:hasPermission('User', 'isAdmin', currentUser)}")
public void invalidateSelectedSessions() {
for (Map.Entry<String, Boolean> entry : selectedSessions.entrySet()) {
if (entry.getValue()) {
HttpSession s = getSession(entry.getKey());
if (s != null) {
log.debug("########### invalidating Http session: " + entry.getKey());
Session seamSession = (Session)s.getAttribute("org.jboss.seam.web.session");
seamSession.invalidate();
}
}
}
selectedSessions.clear();
}
*/
public long getTotalMembers() {
return userDAO.findTotalNoOfUsers();
}
public List<OnlineUser> getOnlineMembers() {
if (onlineMembers == null) loadOnlineMembers();
return onlineMembers;
}
public long getNumberOfOnlineMembers() {
if (onlineMembers == null) loadOnlineMembers();
return onlineMembers.size();
}
public long getNumberOfOnlineGuests() {
return WikiServletListener.getSessions().values().size() - getNumberOfOnlineMembers();
}
private void loadOnlineMembers() {
onlineMembers = new ArrayList<OnlineUser>();
// First get the usernames of members out of all sessions
Map<String,HttpSession> onlineUsernames = new HashMap<String, HttpSession>();
Collection<HttpSession> sessions = WikiServletListener.getSessions().values();
for (HttpSession session : sessions) {
Integer userLevel = (Integer)session.getAttribute(SESSION_ATTR_ACCESSLVL);
if (userLevel != null && userLevel > Role.GUESTROLE_ACCESSLEVEL) {
String username = ((User)session.getAttribute(SESSION_ATTR_USER)).getUsername();
// Try to get the session with the smallest idle time
if (onlineUsernames.containsKey(username)) {
try {
if (session.getLastAccessedTime() > onlineUsernames.get(username).getLastAccessedTime()) {
onlineUsernames.put(username, session);
}
} catch (IllegalStateException ex) {
// Just ignore that:
/*
Caused by: java.lang.IllegalStateException: getLastAccessedTime: Session already invalidated
at org.apache.catalina.session.StandardSession.getLastAccessedTime(StandardSession.java:439)
at org.apache.catalina.session.StandardSessionFacade.getLastAccessedTime(StandardSessionFacade.java:84)
*/
}
} else {
onlineUsernames.put(username, session);
}
}
}
// Then load these guys into a current persistence context
List<User> userInstances = userDAO.findUsersWithUsername(onlineUsernames.keySet());
for (User userInstance : userInstances) {
// Now fill the OnlineUser DTO which is needed by the UI
try {
onlineMembers.add(
new OnlineUser(
userInstance,
onlineUsernames.get(userInstance.getUsername()).getLastAccessedTime()
)
);
} catch (IllegalStateException ex) {
// Just ignore that:
/*
Caused by: java.lang.IllegalStateException: getLastAccessedTime: Session already invalidated
at org.apache.catalina.session.StandardSession.getLastAccessedTime(StandardSession.java:439)
at org.apache.catalina.session.StandardSessionFacade.getLastAccessedTime(StandardSessionFacade.java:84)
*/
}
}
Collections.sort(onlineMembers);
}
public static class OnlineUser implements Comparable {
private User user;
private long lastAccessedTime;
public OnlineUser(User user, long lastAccessedTime) {
this.user = user;
this.lastAccessedTime = lastAccessedTime;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public long getLastAccessedTime() {
return lastAccessedTime;
}
public void setLastAccessedTime(long lastAccessedTime) {
this.lastAccessedTime = lastAccessedTime;
}
public String getIdleTime() {
return WikiUtil.getTimeDifferenceToCurrent(WikiUtil.toDate(lastAccessedTime));
}
public int compareTo(Object o) {
OnlineUser other = (OnlineUser) o;
if (getLastAccessedTime() > other.getLastAccessedTime()) return -1;
return (getLastAccessedTime() == other.getLastAccessedTime() ? 0 : 1);
}
}
}