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