001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (c) Alkacon Software GmbH (http://www.alkacon.com) 006 * 007 * This library is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * This library is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * For further information about Alkacon Software GmbH, please see the 018 * company website: http://www.alkacon.com 019 * 020 * For further information about OpenCms, please see the 021 * project website: http://www.opencms.org 022 * 023 * You should have received a copy of the GNU Lesser General Public 024 * License along with this library; if not, write to the Free Software 025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 026 */ 027 028package org.opencms.main; 029 030import org.opencms.configuration.CmsSystemConfiguration.UserSessionMode; 031import org.opencms.db.CmsUserSettings; 032import org.opencms.file.CmsObject; 033import org.opencms.file.CmsProject; 034import org.opencms.file.CmsRequestContext; 035import org.opencms.file.CmsUser; 036import org.opencms.security.CmsCustomLoginException; 037import org.opencms.security.CmsRole; 038import org.opencms.security.CmsSecurityException; 039import org.opencms.util.CmsRequestUtil; 040import org.opencms.util.CmsStringUtil; 041import org.opencms.util.CmsUUID; 042import org.opencms.workplace.CmsWorkplaceManager; 043import org.opencms.workplace.tools.CmsToolManager; 044 045import java.io.PrintWriter; 046import java.io.StringWriter; 047import java.util.Collections; 048import java.util.Enumeration; 049import java.util.Iterator; 050import java.util.List; 051 052import javax.servlet.http.HttpServletRequest; 053import javax.servlet.http.HttpSession; 054import javax.servlet.http.HttpSessionEvent; 055 056import org.apache.commons.collections.Buffer; 057import org.apache.commons.collections.BufferUtils; 058import org.apache.commons.collections.buffer.CircularFifoBuffer; 059import org.apache.commons.logging.Log; 060 061/** 062 * Keeps track of the sessions running on the OpenCms server and 063 * provides a session info storage which is used to get an overview 064 * about currently authenticated OpenCms users, as well as sending broadcasts between users.<p> 065 * 066 * For each authenticated OpenCms user, a {@link org.opencms.main.CmsSessionInfo} object 067 * holds the information about the users status.<p> 068 * 069 * When a user session is invalidated, the user info will be removed. 070 * This happens when a user log out, or when his session times out.<p> 071 * 072 * <b>Please Note:</b> The current implementation does not provide any permission checking, 073 * so all users can access the methods of this manager. Permission checking 074 * based on the current users OpenCms context may be added in a future OpenCms release.<p> 075 * 076 * @since 6.0.0 077 */ 078public class CmsSessionManager { 079 080 /** The log object for this class. */ 081 private static final Log LOG = CmsLog.getLog(CmsSessionManager.class); 082 083 /** Lock object for synchronized session count updates. */ 084 private Object m_lockSessionCount; 085 086 /** Counter for the currently active sessions. */ 087 private int m_sessionCountCurrent; 088 089 /** Counter for all sessions created so far. */ 090 private int m_sessionCountTotal; 091 092 /** Session storage provider instance. */ 093 private I_CmsSessionStorageProvider m_sessionStorageProvider; 094 095 /** The user session mode. */ 096 private UserSessionMode m_userSessionMode; 097 098 /** 099 * Creates a new instance of the OpenCms session manager.<p> 100 */ 101 protected CmsSessionManager() { 102 103 // create a lock object for the session counter 104 m_lockSessionCount = new Object(); 105 } 106 107 /** 108 * Checks whether a new session can be created for the user, and throws an exception if not.<p> 109 * 110 * @param user the user to check 111 * @throws CmsException if no new session for the user can't be created 112 */ 113 public void checkCreateSessionForUser(CmsUser user) throws CmsException { 114 115 if (getUserSessionMode() == UserSessionMode.single) { 116 List<CmsSessionInfo> infos = getSessionInfos(user.getId()); 117 if (!infos.isEmpty()) { 118 throw new CmsCustomLoginException( 119 org.opencms.security.Messages.get().container( 120 org.opencms.security.Messages.ERR_ALREADY_LOGGED_IN_0)); 121 } 122 } 123 124 } 125 126 /** 127 * Returns the broadcast queue for the given OpenCms session id.<p> 128 * 129 * @param sessionId the OpenCms session id to get the broadcast queue for 130 * 131 * @return the broadcast queue for the given OpenCms session id 132 */ 133 public Buffer getBroadcastQueue(String sessionId) { 134 135 CmsSessionInfo sessionInfo = getSessionInfo(getSessionUUID(sessionId)); 136 if (sessionInfo == null) { 137 // return empty message buffer if the session is gone or not available 138 return BufferUtils.synchronizedBuffer(new CircularFifoBuffer(CmsSessionInfo.QUEUE_SIZE)); 139 } 140 return sessionInfo.getBroadcastQueue(); 141 } 142 143 /** 144 * Returns the number of sessions currently authenticated in the OpenCms security system.<p> 145 * 146 * @return the number of sessions currently authenticated in the OpenCms security system 147 */ 148 public int getSessionCountAuthenticated() { 149 150 // since this method could be called from another thread 151 // we have to prevent access before initialization 152 if (m_sessionStorageProvider == null) { 153 return 0; 154 } 155 return m_sessionStorageProvider.getSize(); 156 } 157 158 /** 159 * Returns the number of current sessions, including the sessions of not authenticated guest users.<p> 160 * 161 * @return the number of current sessions, including the sessions of not authenticated guest users 162 */ 163 public int getSessionCountCurrent() { 164 165 return m_sessionCountCurrent; 166 } 167 168 /** 169 * Returns the number of total sessions generated so far, including already destroyed sessions.<p> 170 * 171 * @return the number of total sessions generated so far, including already destroyed sessions 172 */ 173 public int getSessionCountTotal() { 174 175 return m_sessionCountTotal; 176 } 177 178 /** 179 * Returns the complete user session info of a user from the session storage, 180 * or <code>null</code> if this session id has no session info attached.<p> 181 * 182 * @param sessionId the OpenCms session id to return the session info for 183 * 184 * @return the complete user session info of a user from the session storage 185 */ 186 public CmsSessionInfo getSessionInfo(CmsUUID sessionId) { 187 188 // since this method could be called from another thread 189 // we have to prevent access before initialization 190 if (m_sessionStorageProvider == null) { 191 return null; 192 } 193 return m_sessionStorageProvider.get(sessionId); 194 } 195 196 /** 197 * Returns the OpenCms user session info for the given request, 198 * or <code>null</code> if no user session is available.<p> 199 * 200 * @param req the current request 201 * 202 * @return the OpenCms user session info for the given request, or <code>null</code> if no user session is available 203 */ 204 public CmsSessionInfo getSessionInfo(HttpServletRequest req) { 205 206 HttpSession session = req.getSession(false); 207 if (session == null) { 208 // special case for accessing a session from "outside" requests (e.g. upload applet) 209 String sessionId = req.getHeader(CmsRequestUtil.HEADER_JSESSIONID); 210 return sessionId == null ? null : getSessionInfo(sessionId); 211 } 212 return getSessionInfo(session); 213 } 214 215 /** 216 * Returns the OpenCms user session info for the given http session, 217 * or <code>null</code> if no user session is available.<p> 218 * 219 * @param session the current http session 220 * 221 * @return the OpenCms user session info for the given http session, or <code>null</code> if no user session is available 222 */ 223 public CmsSessionInfo getSessionInfo(HttpSession session) { 224 225 if (session == null) { 226 return null; 227 } 228 CmsUUID sessionId = (CmsUUID)session.getAttribute(CmsSessionInfo.ATTRIBUTE_SESSION_ID); 229 return (sessionId == null) ? null : getSessionInfo(sessionId); 230 } 231 232 /** 233 * Returns the complete user session info of a user from the session storage, 234 * or <code>null</code> if this session id has no session info attached.<p> 235 * 236 * @param sessionId the OpenCms session id to return the session info for, 237 * this must be a String representation of a {@link CmsUUID} 238 * 239 * @return the complete user session info of a user from the session storage 240 * 241 * @see #getSessionInfo(CmsUUID) 242 */ 243 public CmsSessionInfo getSessionInfo(String sessionId) { 244 245 return getSessionInfo(getSessionUUID(sessionId)); 246 } 247 248 /** 249 * Returns all current session info objects.<p> 250 * 251 * @return all current session info objects 252 */ 253 public List<CmsSessionInfo> getSessionInfos() { 254 255 // since this method could be called from another thread 256 // we have to prevent access before initialization 257 if (m_sessionStorageProvider == null) { 258 return Collections.emptyList(); 259 } 260 return m_sessionStorageProvider.getAll(); 261 } 262 263 /** 264 * Returns a list of all active session info objects for the specified user.<p> 265 * 266 * An OpenCms user can have many active sessions. 267 * This is e.g. possible when two people have logged in to the system using the 268 * same username. Even one person can have multiple sessions if he 269 * is logged in to OpenCms with several browser windows at the same time.<p> 270 * 271 * @param userId the id of the user 272 * 273 * @return a list of all active session info objects for the specified user 274 */ 275 public List<CmsSessionInfo> getSessionInfos(CmsUUID userId) { 276 277 // since this method could be called from another thread 278 // we have to prevent access before initialization 279 if (m_sessionStorageProvider == null) { 280 return Collections.emptyList(); 281 } 282 return m_sessionStorageProvider.getAllOfUser(userId); 283 } 284 285 /** 286 * Gets the user session mode.<p> 287 * 288 * @return the user session mode 289 */ 290 public UserSessionMode getUserSessionMode() { 291 292 return m_userSessionMode; 293 } 294 295 /** 296 * Kills all sessions for the given user.<p> 297 * 298 * @param cms the current CMS context 299 * @param user the user for whom the sessions should be killed 300 * 301 * @throws CmsException if something goes wrong 302 */ 303 public void killSession(CmsObject cms, CmsUser user) throws CmsException { 304 305 OpenCms.getRoleManager().checkRole(cms, CmsRole.ACCOUNT_MANAGER); 306 List<CmsSessionInfo> infos = getSessionInfos(user.getId()); 307 for (CmsSessionInfo info : infos) { 308 m_sessionStorageProvider.remove(info.getSessionId()); 309 } 310 } 311 312 /** 313 * Destroys a session given the session id. Only allowed for users which have the "account manager" role.<p> 314 * 315 * @param cms the current CMS context 316 * @param sessionid the session id 317 * 318 * @throws CmsException if something goes wrong 319 */ 320 public void killSession(CmsObject cms, CmsUUID sessionid) throws CmsException { 321 322 OpenCms.getRoleManager().checkRole(cms, CmsRole.ACCOUNT_MANAGER); 323 m_sessionStorageProvider.remove(sessionid); 324 325 } 326 327 /** 328 * Sends a broadcast to all sessions of all currently authenticated users.<p> 329 * 330 * @param cms the OpenCms user context of the user sending the broadcast 331 * 332 * @param message the message to broadcast 333 */ 334 public void sendBroadcast(CmsObject cms, String message) { 335 336 if (CmsStringUtil.isEmptyOrWhitespaceOnly(message)) { 337 // don't broadcast empty messages 338 return; 339 } 340 // create the broadcast 341 CmsBroadcast broadcast = new CmsBroadcast(cms.getRequestContext().getCurrentUser(), message); 342 // send the broadcast to all authenticated sessions 343 Iterator<CmsSessionInfo> i = m_sessionStorageProvider.getAll().iterator(); 344 while (i.hasNext()) { 345 CmsSessionInfo sessionInfo = i.next(); 346 if (m_sessionStorageProvider.get(sessionInfo.getSessionId()) != null) { 347 // double check for concurrent modification 348 sessionInfo.getBroadcastQueue().add(broadcast); 349 } 350 } 351 } 352 353 /** 354 * Sends a broadcast to the specified user session.<p> 355 * 356 * @param cms the OpenCms user context of the user sending the broadcast 357 * 358 * @param message the message to broadcast 359 * @param sessionId the OpenCms session uuid target (receiver) of the broadcast 360 */ 361 public void sendBroadcast(CmsObject cms, String message, String sessionId) { 362 363 if (CmsStringUtil.isEmptyOrWhitespaceOnly(message)) { 364 // don't broadcast empty messages 365 return; 366 } 367 // send the broadcast only to the selected session 368 CmsSessionInfo sessionInfo = m_sessionStorageProvider.get(new CmsUUID(sessionId)); 369 if (sessionInfo != null) { 370 // double check for concurrent modification 371 sessionInfo.getBroadcastQueue().add(new CmsBroadcast(cms.getRequestContext().getCurrentUser(), message)); 372 } 373 } 374 375 /** 376 * Sends a broadcast to all sessions of a given user.<p> 377 * 378 * The user sending the message may be a real user like 379 * <code>cms.getRequestContext().currentUser()</code> or 380 * <code>null</code> for a system message.<p> 381 * 382 * @param fromUser the user sending the broadcast 383 * @param message the message to broadcast 384 * @param toUser the target (receiver) of the broadcast 385 */ 386 public void sendBroadcast(CmsUser fromUser, String message, CmsUser toUser) { 387 388 if (CmsStringUtil.isEmptyOrWhitespaceOnly(message)) { 389 // don't broadcast empty messages 390 return; 391 } 392 // create the broadcast 393 CmsBroadcast broadcast = new CmsBroadcast(fromUser, message); 394 List<CmsSessionInfo> userSessions = getSessionInfos(toUser.getId()); 395 Iterator<CmsSessionInfo> i = userSessions.iterator(); 396 // send the broadcast to all sessions of the selected user 397 while (i.hasNext()) { 398 CmsSessionInfo sessionInfo = i.next(); 399 if (m_sessionStorageProvider.get(sessionInfo.getSessionId()) != null) { 400 // double check for concurrent modification 401 sessionInfo.getBroadcastQueue().add(broadcast); 402 } 403 } 404 } 405 406 /** 407 * Switches the current user to the given user. The session info is rebuild as if the given user 408 * performs a login at the workplace. 409 * 410 * @param cms the current CmsObject 411 * @param req the current request 412 * @param user the user to switch to 413 * 414 * @throws CmsException if something goes wrong 415 */ 416 public void switchUser(CmsObject cms, HttpServletRequest req, CmsUser user) throws CmsException { 417 418 // only user with root administrator role are allowed to switch the user 419 OpenCms.getRoleManager().checkRole(cms, CmsRole.ROOT_ADMIN.forOrgUnit(user.getOuFqn())); 420 CmsSessionInfo info = getSessionInfo(req); 421 HttpSession session = req.getSession(false); 422 if ((info == null) || (session == null)) { 423 throw new CmsException(Messages.get().container(Messages.ERR_NO_SESSIONINFO_SESSION_0)); 424 } 425 426 if (!OpenCms.getRoleManager().hasRole(cms, user.getName(), CmsRole.WORKPLACE_USER)) { 427 throw new CmsSecurityException(Messages.get().container(Messages.ERR_NO_WORKPLACE_PERMISSIONS_0)); 428 } 429 430 // get the user settings for the given user and set the start project and the site root 431 CmsUserSettings settings = new CmsUserSettings(user); 432 String ouFqn = user.getOuFqn(); 433 CmsProject userProject = cms.readProject( 434 ouFqn + OpenCms.getWorkplaceManager().getDefaultUserSettings().getStartProject()); 435 try { 436 userProject = cms.readProject(settings.getStartProject()); 437 } catch (Exception e) { 438 // ignore, use default 439 } 440 String userSiteRoot = settings.getStartSite(); 441 CmsRequestContext context = new CmsRequestContext( 442 user, 443 userProject, 444 null, 445 cms.getRequestContext().getRequestMatcher(), 446 userSiteRoot, 447 cms.getRequestContext().isSecureRequest(), 448 null, 449 null, 450 null, 451 0, 452 null, 453 null, 454 ouFqn); 455 // delete the stored workplace settings, so the session has to receive them again 456 session.removeAttribute(CmsWorkplaceManager.SESSION_WORKPLACE_SETTINGS); 457 458 // create a new CmsSessionInfo and store it inside the session map 459 CmsSessionInfo newInfo = new CmsSessionInfo(context, info.getSessionId(), info.getMaxInactiveInterval()); 460 addSessionInfo(newInfo); 461 // set the site root, project and ou fqn to current cms context 462 cms.getRequestContext().setSiteRoot(userSiteRoot); 463 cms.getRequestContext().setCurrentProject(userProject); 464 cms.getRequestContext().setOuFqn(user.getOuFqn()); 465 } 466 467 /** 468 * @see java.lang.Object#toString() 469 */ 470 @Override 471 public String toString() { 472 473 StringBuffer output = new StringBuffer(); 474 Iterator<CmsSessionInfo> i = m_sessionStorageProvider.getAll().iterator(); 475 output.append("[CmsSessions]:\n"); 476 while (i.hasNext()) { 477 CmsSessionInfo sessionInfo = i.next(); 478 output.append(sessionInfo.getSessionId().toString()); 479 output.append(" : "); 480 output.append(sessionInfo.getUserId().toString()); 481 output.append('\n'); 482 } 483 return output.toString(); 484 } 485 486 /** 487 * Updates the the OpenCms session data used for quick authentication of users.<p> 488 * 489 * This is required if the user data (current group or project) was changed in 490 * the requested document.<p> 491 * 492 * The user data is only updated if the user was authenticated to the system. 493 * 494 * @param cms the current OpenCms user context 495 * @param req the current request 496 */ 497 public void updateSessionInfo(CmsObject cms, HttpServletRequest req) { 498 499 if (!cms.getRequestContext().isUpdateSessionEnabled()) { 500 // this request must not update the user session info 501 // this is true for long running "thread" requests, e.g. during project publish 502 return; 503 } 504 505 if (cms.getRequestContext().getUri().equals(CmsToolManager.VIEW_JSPPAGE_LOCATION)) { 506 // this request must not update the user session info 507 // if not the switch user feature would not work 508 return; 509 } 510 511 if (!cms.getRequestContext().getCurrentUser().isGuestUser()) { 512 // Guest user requests don't need to update the OpenCms user session information 513 514 // get the session info object for the user 515 CmsSessionInfo sessionInfo = getSessionInfo(req); 516 if (sessionInfo != null) { 517 // update the users session information 518 sessionInfo.update(cms.getRequestContext()); 519 addSessionInfo(sessionInfo); 520 } else { 521 HttpSession session = req.getSession(false); 522 // only create session info if a session is already available 523 if (session != null) { 524 // create a new session info for the user 525 sessionInfo = new CmsSessionInfo( 526 cms.getRequestContext(), 527 new CmsUUID(), 528 session.getMaxInactiveInterval()); 529 // append the session info to the http session 530 session.setAttribute(CmsSessionInfo.ATTRIBUTE_SESSION_ID, sessionInfo.getSessionId().clone()); 531 // update the session info user data 532 addSessionInfo(sessionInfo); 533 } 534 } 535 } 536 } 537 538 /** 539 * Updates the the OpenCms session data used for quick authentication of users.<p> 540 * 541 * This is required if the user data (current group or project) was changed in 542 * the requested document.<p> 543 * 544 * The user data is only updated if the user was authenticated to the system. 545 * 546 * @param cms the current OpenCms user context 547 * @param session the current session 548 */ 549 public void updateSessionInfo(CmsObject cms, HttpSession session) { 550 551 if (session == null) { 552 return; 553 } 554 555 if (!cms.getRequestContext().isUpdateSessionEnabled()) { 556 // this request must not update the user session info 557 // this is true for long running "thread" requests, e.g. during project publish 558 return; 559 } 560 561 if (cms.getRequestContext().getUri().equals(CmsToolManager.VIEW_JSPPAGE_LOCATION)) { 562 // this request must not update the user session info 563 // if not the switch user feature would not work 564 return; 565 } 566 567 if (!cms.getRequestContext().getCurrentUser().isGuestUser()) { 568 // Guest user requests don't need to update the OpenCms user session information 569 570 // get the session info object for the user 571 CmsSessionInfo sessionInfo = getSessionInfo(session); 572 if (sessionInfo != null) { 573 // update the users session information 574 sessionInfo.update(cms.getRequestContext()); 575 addSessionInfo(sessionInfo); 576 } else { 577 sessionInfo = new CmsSessionInfo( 578 cms.getRequestContext(), 579 new CmsUUID(), 580 session.getMaxInactiveInterval()); 581 // append the session info to the http session 582 session.setAttribute(CmsSessionInfo.ATTRIBUTE_SESSION_ID, sessionInfo.getSessionId().clone()); 583 // update the session info user data 584 addSessionInfo(sessionInfo); 585 } 586 } 587 } 588 589 /** 590 * Updates all session info objects, so that invalid projects 591 * are replaced by the Online project.<p> 592 * 593 * @param cms the cms context 594 */ 595 public void updateSessionInfos(CmsObject cms) { 596 597 // get all sessions 598 List<CmsSessionInfo> userSessions = getSessionInfos(); 599 Iterator<CmsSessionInfo> i = userSessions.iterator(); 600 while (i.hasNext()) { 601 CmsSessionInfo sessionInfo = i.next(); 602 // check is the project stored in this session is not existing anymore 603 // if so, set it to the online project 604 CmsUUID projectId = sessionInfo.getProject(); 605 try { 606 cms.readProject(projectId); 607 } catch (CmsException e) { 608 // the project does not longer exist, update the project information with the online project 609 sessionInfo.setProject(CmsProject.ONLINE_PROJECT_ID); 610 addSessionInfo(sessionInfo); 611 } 612 } 613 } 614 615 /** 616 * Adds a new session info into the session storage.<p> 617 * 618 * @param sessionInfo the session info to store for the id 619 */ 620 protected void addSessionInfo(CmsSessionInfo sessionInfo) { 621 622 if (getUserSessionMode() == UserSessionMode.standard) { 623 m_sessionStorageProvider.put(sessionInfo); 624 } else if (getUserSessionMode() == UserSessionMode.single) { 625 CmsUUID userId = sessionInfo.getUserId(); 626 List<CmsSessionInfo> infos = getSessionInfos(userId); 627 if (infos.isEmpty() 628 || ((infos.size() == 1) && infos.get(0).getSessionId().equals(sessionInfo.getSessionId()))) { 629 m_sessionStorageProvider.put(sessionInfo); 630 } else { 631 throw new RuntimeException("Can't create another session for the same user."); 632 } 633 } 634 } 635 636 /** 637 * Returns the UUID representation for the given session id String.<p> 638 * 639 * @param sessionId the session id String to return the UUID representation for 640 * 641 * @return the UUID representation for the given session id String 642 */ 643 protected CmsUUID getSessionUUID(String sessionId) { 644 645 return new CmsUUID(sessionId); 646 } 647 648 /** 649 * Sets the storage provider.<p> 650 * 651 * @param sessionStorageProvider the storage provider implementation 652 */ 653 protected void initialize(I_CmsSessionStorageProvider sessionStorageProvider) { 654 655 m_sessionStorageProvider = sessionStorageProvider; 656 m_sessionStorageProvider.initialize(); 657 } 658 659 /** 660 * Called by the {@link OpenCmsListener} when a http session is created.<p> 661 * 662 * @param event the http session event 663 * 664 * @see javax.servlet.http.HttpSessionListener#sessionCreated(javax.servlet.http.HttpSessionEvent) 665 * @see OpenCmsListener#sessionCreated(HttpSessionEvent) 666 */ 667 protected void sessionCreated(HttpSessionEvent event) { 668 669 HttpServletRequest request = OpenCmsServlet.currentRequest.get(); 670 String tid = "[" + Thread.currentThread().getId() + "] "; 671 synchronized (m_lockSessionCount) { 672 m_sessionCountCurrent = (m_sessionCountCurrent <= 0) ? 1 : (m_sessionCountCurrent + 1); 673 m_sessionCountTotal++; 674 if (LOG.isInfoEnabled()) { 675 LOG.info( 676 tid 677 + Messages.get().getBundle().key( 678 Messages.LOG_SESSION_CREATED_2, 679 new Integer(m_sessionCountTotal), 680 new Integer(m_sessionCountCurrent))); 681 } 682 } 683 684 if (LOG.isDebugEnabled()) { 685 LOG.debug(tid + Messages.get().getBundle().key(Messages.LOG_SESSION_CREATED_1, event.getSession().getId())); 686 if (request != null) { 687 LOG.debug(tid + "Session created in request: " + request.getRequestURL()); 688 } 689 StringWriter sw = new StringWriter(); 690 new Throwable("").printStackTrace(new PrintWriter(sw)); 691 String stackTrace = sw.toString(); 692 LOG.debug(tid + "Stack = \n" + stackTrace); 693 } 694 } 695 696 /** 697 * Called by the {@link OpenCmsListener} when a http session is destroyed.<p> 698 * 699 * @param event the http session event 700 * 701 * @see javax.servlet.http.HttpSessionListener#sessionDestroyed(javax.servlet.http.HttpSessionEvent) 702 * @see OpenCmsListener#sessionDestroyed(HttpSessionEvent) 703 */ 704 protected void sessionDestroyed(HttpSessionEvent event) { 705 706 synchronized (m_lockSessionCount) { 707 m_sessionCountCurrent = (m_sessionCountCurrent <= 0) ? 0 : (m_sessionCountCurrent - 1); 708 if (LOG.isInfoEnabled()) { 709 LOG.info( 710 Messages.get().getBundle().key( 711 Messages.LOG_SESSION_DESTROYED_2, 712 new Integer(m_sessionCountTotal), 713 new Integer(m_sessionCountCurrent))); 714 } 715 } 716 717 CmsSessionInfo sessionInfo = getSessionInfo(event.getSession()); 718 CmsUUID userId = null; 719 if (sessionInfo != null) { 720 userId = sessionInfo.getUserId(); 721 m_sessionStorageProvider.remove(sessionInfo.getSessionId()); 722 } 723 724 if ((userId != null) && (getSessionInfos(userId).size() == 0)) { 725 // remove the temporary locks of this user from memory 726 OpenCmsCore.getInstance().getLockManager().removeTempLocks(userId); 727 } 728 729 HttpSession session = event.getSession(); 730 Enumeration<?> attrNames = session.getAttributeNames(); 731 while (attrNames.hasMoreElements()) { 732 String attrName = (String)attrNames.nextElement(); 733 Object attribute = session.getAttribute(attrName); 734 if (attribute instanceof I_CmsSessionDestroyHandler) { 735 try { 736 ((I_CmsSessionDestroyHandler)attribute).onSessionDestroyed(); 737 } catch (Exception e) { 738 LOG.error(e.getLocalizedMessage(), e); 739 } 740 } 741 } 742 743 if (LOG.isDebugEnabled()) { 744 LOG.debug(Messages.get().getBundle().key(Messages.LOG_SESSION_DESTROYED_1, event.getSession().getId())); 745 } 746 } 747 748 /** 749 * Sets the user session mode.<p> 750 * 751 * @param userSessionMode the user session mode 752 */ 753 protected void setUserSessionMode(UserSessionMode userSessionMode) { 754 755 m_userSessionMode = userSessionMode; 756 } 757 758 /** 759 * Removes all stored session info objects.<p> 760 * 761 * @throws Exception if something goes wrong 762 */ 763 protected void shutdown() throws Exception { 764 765 if (m_sessionStorageProvider != null) { 766 m_sessionStorageProvider.shutdown(); 767 } 768 } 769 770 /** 771 * Validates the sessions stored in this manager and removes 772 * any sessions that have become invalidated.<p> 773 */ 774 protected void validateSessionInfos() { 775 776 // since this method could be called from another thread 777 // we have to prevent access before initialization 778 if (m_sessionStorageProvider == null) { 779 return; 780 } 781 m_sessionStorageProvider.validate(); 782 } 783}