package com.joe.jsf.web.view; import java.text.NumberFormat; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.List; import java.util.Map; import java.util.TimeZone; import java.util.TreeMap; import javax.faces.bean.ApplicationScoped; import javax.faces.bean.ManagedBean; import javax.faces.event.ActionEvent; import javax.servlet.http.HttpSession; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultTreeModel; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import com.icesoft.faces.component.tree.IceUserObject; import com.joe.jsf.helper.ManagedBeanUtility; import com.joe.utilities.core.data.Node; import com.joe.utilities.core.data.Tree; import com.joe.utilities.core.lookup.CacheManager; import com.joe.utilities.core.serviceLocator.ServiceLocator; import com.joe.utilities.core.util.MemoryCounter; import com.joe.utilities.core.util.MemoryNode; /** * The Class SessionMonitorBean. This class records session statistic related to * estimated memory size for active sessions. This class also serves as an * application scope JSF managed bean to service the "sessionMonitor.jspx" page. * * @author Dave Ousey */ @ManagedBean(name="SessionMonitorBean") @ApplicationScoped public class SessionMonitorBean { /** The Constant log. */ private static final Log log = LogFactory.getLog(SessionMonitorBean.class); /** Session monitoring enabled?. */ private static boolean enabled = false; /** The current session state by session id. */ private static Map<String, SessionMonitorState> currentSessionStateBySessionID = Collections.synchronizedMap(new TreeMap<String, SessionMonitorState>()); /** Selected monitor state to view recorded detail. */ private static SessionMonitorState selectedSessionMonitorState; /** The cache manager text. */ private String cacheManagerText = "Not estimated"; /** The cache manager memory tree. */ private Tree<MemoryNode> cacheManagerMemoryTree = null; /** The cache manager memory tree model. */ private DefaultTreeModel cacheManagerMemoryTreeModel = null; /** The service locator text. */ private String serviceLocatorText = "Not estimated"; /** The service locator memory tree. */ private Tree<MemoryNode> serviceLocatorMemoryTree = null; /** The cache manager memory tree model. */ private DefaultTreeModel serviceLocatorMemoryTreeModel = null; /** The stack level input. */ private static int stackLevel = 5; /** The memory threshold input. */ private static long memoryThreshold = 64L; /** The filter me decision classes. */ private static boolean filterMEDecisionClasses = false; /** * Default constructor. */ public SessionMonitorBean() { } /** * Records session data. * * @param session the session */ public static void recordSessionData(HttpSession session) { // Do nothing if session monitoring is not turned on. if (!enabled) return; // No session: Logoff scenario if (session == null) return; // Get session ID String sessionID = session.getId(); if (sessionID == null) return; // Cache user on session // Lookup session state for session ID - create if necessary SessionMonitorState sessionState = currentSessionStateBySessionID.get(sessionID); if (sessionState == null) { sessionState = new SessionMonitorState(sessionID); currentSessionStateBySessionID.put(sessionID, sessionState); } // Update session state. If given session ID is marked as the record // session state, then pass a flag to generate complete memory tree. boolean createMemoryTree = (selectedSessionMonitorState != null && selectedSessionMonitorState.getSessionID().equals(sessionID)); sessionState.updateSessionData(session, createMemoryTree, stackLevel, memoryThreshold*1024L); } /** * Checks if is enabled. * * @return true, if is enabled */ public boolean isEnabled() { return enabled; } /** * Invoke enable. * * @param event the event * * @return the string */ public String invokeEnable(ActionEvent event) { enabled = true; return null; } /** * Invoke disable. * * @param event the event * * @return the string */ public String invokeDisable(ActionEvent event) { enabled = false; return null; } /** * Clear. Action referenced by JSP * * @param event the event * * @return the string */ public String invokeClear(ActionEvent event) { currentSessionStateBySessionID.clear(); selectedSessionMonitorState = null; return null; } /** * Invoke refresh. * * @param event the event * * @return the string */ public String invokeRefresh(ActionEvent event) { return null; } /** * Invoke detail. * * @param event the event * * @return the string */ public String invokeRecordSession(ActionEvent event) { String sessionID = ManagedBeanUtility.getRequestParameter("sessionID"); selectedSessionMonitorState = currentSessionStateBySessionID.get(sessionID); return null; } /** * Invoke estimate cache manager. * * @param event the event * * @return the string */ public String invokeEstimateCacheManager(ActionEvent event) { MemoryCounter memoryCounter = new MemoryCounter(false, stackLevel, memoryThreshold*1024L, true); long startTime = System.currentTimeMillis(); memoryCounter.estimate(CacheManager.getInstance()); long endTime = System.currentTimeMillis(); cacheManagerText = "Last estimated at " + new Date(); cacheManagerMemoryTree = memoryCounter.getMemoryTree(); cacheManagerMemoryTreeModel = createICEFacesDefaultTreeModelFromTree(cacheManagerMemoryTree); log.warn("Session monitoring enabled: It took more than " + (endTime - startTime) + " milliseconds to estimate memory size for CacheManager"); return null; } /** * Invoke estimate service locator. * * @param event the event * * @return the string */ public String invokeEstimateServiceLocator(ActionEvent event) { MemoryCounter memoryCounter = new MemoryCounter(false, stackLevel, memoryThreshold*1024L, true); long startTime = System.currentTimeMillis(); memoryCounter.estimate(ServiceLocator.getInstance()); long endTime = System.currentTimeMillis(); serviceLocatorText = "Last estimated at " + new Date(); serviceLocatorMemoryTree = memoryCounter.getMemoryTree(); serviceLocatorMemoryTreeModel = createICEFacesDefaultTreeModelFromTree(serviceLocatorMemoryTree); log.warn("Session monitoring enabled: It took more than " + (endTime - startTime) + " milliseconds to estimate memory size for ServiceLocator"); return null; } /** * Checks if is selected session monitor state exists. * * @return true, if is selected session monitor state exists */ public boolean isSelectedSessionMonitorStateExists() { return selectedSessionMonitorState != null; } /** * Gets the session monitor list. * * @return the session monitor list */ public Collection<SessionMonitorState> getSessionMonitorList() { return new ArrayList<SessionMonitorState>(currentSessionStateBySessionID.values()); } /** * Checks if is monitor state exists. * * @return true, if is monitor state exists */ public boolean isMonitorStateExists() { return currentSessionStateBySessionID.size() > 0; } /** * Gets the last refresh time. * * @return the last refresh time */ public Date getLastRefreshTime() { return new Date(); } /** * Gets the selected session monitor state. * * @return the selected session monitor state */ public SessionMonitorState getSelectedSessionMonitorState() { return selectedSessionMonitorState; } /** * Returns the default time zone from the JVM so that it can be used by the * date/time converter to convert to the same time zone. * * @return the time zone */ public TimeZone getTimeZone() { return TimeZone.getDefault(); } /** * Gets the cache manager text. * * @return the cache manager text */ public String getCacheManagerText() { return cacheManagerText; } /** * Gets the service locator text. * * @return the service locator text */ public String getServiceLocatorText() { return serviceLocatorText; } /** * Gets the total measured session size. * * @return the total measured session size */ public long getTotalMeasuredSessionSize() { long total = 0; for (SessionMonitorState sessionState : currentSessionStateBySessionID.values()) { total += sessionState.getCurrentSessionSize(); } return total; } /** * Creates the ice faces default tree model from tree. * * @param tree the tree * * @return the default tree model */ public static DefaultTreeModel createICEFacesDefaultTreeModelFromTree(Tree<MemoryNode> tree) { if (tree.getRootElement() != null) { DefaultMutableTreeNode rootIceNode = createICEFacesDefaultMutableTreeNode(tree.getRootElement()); if (rootIceNode == null) { // Create default root node rootIceNode = new DefaultMutableTreeNode(); IceUserObject rootObject = new IceUserObject(rootIceNode); rootIceNode.setUserObject(rootObject); rootObject.setExpanded(false); rootObject.setLeaf(true); rootObject.setText("All items filtered for "); MemoryNode memoryNode = tree.getRootElement().getData(); rootObject.setText("Filtered: " + memoryNode.getClassName() + ": [" + NumberFormat.getInstance().format(memoryNode.getMemory()) + " KB]"); } return new DefaultTreeModel(rootIceNode); } else throw new IllegalArgumentException("Tree has no root element"); } /** * Creates the ice faces default mutable tree node. * * @param node the node * * @return the default mutable tree node */ public static DefaultMutableTreeNode createICEFacesDefaultMutableTreeNode(Node<MemoryNode> node) { List<Node<MemoryNode>> childNodes = node.getChildren(); // Filter check if (filterMEDecisionClasses && !isMEDecisionNode(node)) return null; DefaultMutableTreeNode treeNode = new DefaultMutableTreeNode(); IceUserObject rootObject = new IceUserObject(treeNode); treeNode.setUserObject(rootObject); rootObject.setExpanded(false); // Setup text MemoryNode memoryNode = node.getData(); if (memoryNode.getFieldName() != null) rootObject.setText(memoryNode.getClassName() + ": [Field = '" + memoryNode.getFieldName() + "'] [" + NumberFormat.getInstance().format(memoryNode.getMemory()) + " KB]"); else rootObject.setText(memoryNode.getClassName() + ": [" + NumberFormat.getInstance().format(memoryNode.getMemory()) + " KB]"); // Deal with child nodes boolean addedChild = false; for (Node<MemoryNode> childNode : childNodes) { DefaultMutableTreeNode childIceNode = createICEFacesDefaultMutableTreeNode(childNode); if (childIceNode != null) { treeNode.add(childIceNode); addedChild = true; } } rootObject.setLeaf(!addedChild); return treeNode; } /** * Checks if is mE decision node. * @param node the node * * @return true, if is mE decision node */ private static boolean isMEDecisionNode(Node<MemoryNode> node) { // Ask self if (node.getData().getClassName().startsWith("com.med")) { return true; } else { // Ask children for (Node<MemoryNode> childNode : node.getChildren()) { if (isMEDecisionNode(childNode)) return true; } } return false; } /** * Gets the cache manager memory tree. * * @return the cache manager memory tree */ public DefaultTreeModel getCacheManagerMemoryTreeModel() { return cacheManagerMemoryTreeModel; } /** * Gets the service locator memory tree. * * @return the service locator memory tree */ public DefaultTreeModel getServiceLocatorMemoryTreeModel() { return serviceLocatorMemoryTreeModel; } /** * Checks if is selected session detail tree exists. * * @return true, if is selected session detail tree exists */ public boolean isSelectedSessionDetailTreeExists() { return selectedSessionMonitorState != null && selectedSessionMonitorState.getDetailMemoryTreeModel() != null; } /** * Checks if is service locator memory tree exists. * * @return true, if is service locator memory tree exists */ public boolean isServiceLocatorMemoryTreeExists() { return serviceLocatorMemoryTree != null; } /** * Checks if is cache manager memory tree exists. * * @return true, if is cache manager memory tree exists */ public boolean isCacheManagerMemoryTreeExists() { return cacheManagerMemoryTree != null; } /** * Gets the selected session memory tree. * * @return the selected session memory tree */ public DefaultTreeModel getSelectedSessionMemoryTreeModel() { return selectedSessionMonitorState.getDetailMemoryTreeModel(); } /** * Gets the stack level. * * @return the stack level */ public int getStackLevel() { return stackLevel; } /** * Sets the stack level . * * @param stackLevelInput the new stack level */ public void setStackLevel(int stackLevel) { SessionMonitorBean.stackLevel = stackLevel; } /** * Gets the memory threshold . * * @return the memory threshold */ public long getMemoryThreshold() { return memoryThreshold; } /** * Sets the memory threshold input. * * @param memoryThresholdInput the new memory threshold input */ public void setMemoryThreshold(long memoryThreshold) { SessionMonitorBean.memoryThreshold = memoryThreshold; } /** * Checks if is filter me decision classes. * * @return true, if is filter me decision classes */ public boolean isFilterMEDecisionClasses() { return SessionMonitorBean.filterMEDecisionClasses; } /** * Sets the filter me decision classes. * * @param filterMEDecisionClasses the new filter me decision classes */ public void setFilterMEDecisionClasses(boolean filterMEDecisionClasses) { SessionMonitorBean.filterMEDecisionClasses = filterMEDecisionClasses; } }