1 16 package org.mortbay.jetty.servlet; 17 18 import java.util.ArrayList ; 19 import java.util.Collections ; 20 import java.util.Enumeration ; 21 import java.util.EventListener ; 22 import java.util.HashMap ; 23 import java.util.Iterator ; 24 import java.util.List ; 25 import java.util.Map ; 26 import java.util.Random ; 27 28 import javax.servlet.ServletContext ; 29 import javax.servlet.http.Cookie ; 30 import javax.servlet.http.HttpServletRequest ; 31 import javax.servlet.http.HttpSession ; 32 import javax.servlet.http.HttpSessionAttributeListener ; 33 import javax.servlet.http.HttpSessionBindingEvent ; 34 import javax.servlet.http.HttpSessionBindingListener ; 35 import javax.servlet.http.HttpSessionContext ; 36 import javax.servlet.http.HttpSessionEvent ; 37 import javax.servlet.http.HttpSessionListener ; 38 39 import org.apache.commons.logging.Log; 40 import org.mortbay.log.LogFactory; 41 import org.mortbay.http.HttpOnlyCookie; 42 import org.mortbay.util.LazyList; 43 import org.mortbay.util.LogSupport; 44 import org.mortbay.util.MultiMap; 45 46 47 48 63 public abstract class AbstractSessionManager implements SessionManager 64 { 65 private static Log log = LogFactory.getLog(AbstractSessionManager.class); 66 67 68 public final static int __distantFuture = 60*60*24*7*52*20; 69 private final static String __NEW_SESSION_ID="org.mortbay.jetty.newSessionId"; 70 71 72 73 protected static MultiMap __allSessions=new MultiMap(); 74 75 76 private int _dftMaxIdleSecs = -1; 79 private int _scavengePeriodMs = 30000; 80 private String _workerName ; 81 protected transient ArrayList _sessionListeners=new ArrayList (); 82 protected transient ArrayList _sessionAttributeListeners=new ArrayList (); 83 protected transient Map _sessions; 84 protected transient Random _random; 85 protected transient ServletHandler _handler; 86 protected int _minSessions = 0; 87 protected int _maxSessions = 0; 88 protected boolean _crossContextSessionIDs=false; 89 protected boolean _secureCookies=false; 90 protected boolean _httpOnly=false; 91 protected boolean _invalidateGlobal=true; 92 93 private transient SessionScavenger _scavenger = null; 94 95 96 public AbstractSessionManager() 97 { 98 this(null); 99 } 100 101 102 public AbstractSessionManager(Random random) 103 { 104 _random=random; 105 } 106 107 108 109 110 115 public boolean getUseRequestedId() 116 { 117 return _crossContextSessionIDs; 118 } 119 120 121 126 public void setUseRequestedId(boolean useRequestedId) 127 { 128 _crossContextSessionIDs = useRequestedId; 129 } 130 131 132 136 public boolean getCrossContextSessionIDs() 137 { 138 return _crossContextSessionIDs; 139 } 140 141 142 150 public void setCrossContextSessionIDs(boolean useRequestedId) 151 { 152 _crossContextSessionIDs = useRequestedId; 153 } 154 155 156 public void initialize(ServletHandler handler) 157 { 158 _handler=handler; 159 } 160 161 162 public Map getSessionMap() 163 { 164 return Collections.unmodifiableMap(_sessions); 165 } 166 167 168 public int getSessions () 169 { 170 return _sessions.size (); 171 } 172 173 174 public int getMinSessions () 175 { 176 return _minSessions; 177 } 178 179 180 public int getMaxSessions () 181 { 182 return _maxSessions; 183 } 184 185 186 public void resetStats () 187 { 188 _minSessions = _sessions.size (); 189 _maxSessions = _sessions.size (); 190 } 191 192 193 203 private String newSessionId(HttpServletRequest request,long created) 204 { 205 synchronized(__allSessions) 206 { 207 if (_crossContextSessionIDs) 211 { 212 String requested_id=(String )request.getAttribute(__NEW_SESSION_ID); 213 if (requested_id==null) 214 requested_id=request.getRequestedSessionId(); 215 if (requested_id !=null && 216 requested_id!=null && __allSessions.containsKey(requested_id) && !_sessions.containsKey(requested_id)) 217 return requested_id; 218 } 219 220 String id=null; 222 while (id==null || id.length()==0 || __allSessions.containsKey(id)) 223 { 224 long r = _random.nextLong(); 225 if (r<0)r=-r; 226 id=Long.toString(r,30+(int)(created%7)); 227 String worker = (String )request.getAttribute("org.mortbay.http.ajp.JVMRoute"); 228 if (worker!=null) 229 id+="."+worker; 230 else if (_workerName!=null) 231 id+="."+_workerName; 232 } 233 return id; 234 } 235 } 236 237 238 public HttpSession getHttpSession(String id) 239 { 240 synchronized(this) 241 { 242 return (HttpSession )_sessions.get(id); 243 } 244 } 245 246 247 public HttpSession newHttpSession(HttpServletRequest request) 248 { 249 Session session = newSession(request); 250 session.setMaxInactiveInterval(_dftMaxIdleSecs); 251 synchronized(__allSessions) 252 { 253 synchronized(this) 254 { 255 _sessions.put(session.getId(),session); 256 __allSessions.add(session.getId(), session); 257 if (_sessions.size() > this._maxSessions) 258 this._maxSessions = _sessions.size (); 259 } 260 } 261 262 HttpSessionEvent event=new HttpSessionEvent (session); 263 264 for(int i=0;i<_sessionListeners.size();i++) 265 ((HttpSessionListener )_sessionListeners.get(i)) 266 .sessionCreated(event); 267 268 if (getCrossContextSessionIDs()) 269 request.setAttribute(__NEW_SESSION_ID, session.getId()); 270 return session; 271 } 272 273 274 public Cookie getSessionCookie(HttpSession session,boolean requestIsSecure) 275 { 276 if (_handler.isUsingCookies()) 277 { 278 Cookie cookie = _handler.getSessionManager().getHttpOnly() 279 ?new HttpOnlyCookie(SessionManager.__SessionCookie,session.getId()) 280 :new Cookie (SessionManager.__SessionCookie,session.getId()); 281 String domain=_handler.getServletContext().getInitParameter(SessionManager.__SessionDomain); 282 String maxAge=_handler.getServletContext().getInitParameter(SessionManager.__MaxAge); 283 String path=_handler.getServletContext().getInitParameter(SessionManager.__SessionPath); 284 if (path==null) 285 path=getCrossContextSessionIDs()?"/":_handler.getHttpContext().getContextPath(); 286 if (path==null || path.length()==0) 287 path="/"; 288 289 if (domain!=null) 290 cookie.setDomain(domain); 291 if (maxAge!=null) 292 cookie.setMaxAge(Integer.parseInt(maxAge)); 293 else 294 cookie.setMaxAge(-1); 295 296 cookie.setSecure(requestIsSecure && getSecureCookies()); 297 cookie.setPath(path); 298 299 return cookie; 300 } 301 return null; 302 } 303 304 305 protected abstract Session newSession(HttpServletRequest request); 306 307 308 313 public String getWorkerName() 314 { 315 return _workerName; 316 } 317 318 319 324 public void setWorkerName(String workerName) 325 { 326 _workerName = workerName; 327 } 328 329 330 333 public int getMaxInactiveInterval() 334 { 335 return _dftMaxIdleSecs; 336 } 337 338 339 342 public void setMaxInactiveInterval(int seconds) 343 { 344 _dftMaxIdleSecs = seconds; 345 if (_dftMaxIdleSecs>0 && _scavengePeriodMs>_dftMaxIdleSecs*100) 346 setScavengePeriod((_dftMaxIdleSecs+9)/10); 347 } 348 349 350 351 354 public int getScavengePeriod() 355 { 356 return _scavengePeriodMs/1000; 357 } 358 359 360 363 public void setScavengePeriod(int seconds) 364 { 365 if (seconds==0) 366 seconds=60; 367 368 int old_period=_scavengePeriodMs; 369 int period = seconds*1000; 370 if (period>60000) 371 period=60000; 372 if (period<1000) 373 period=1000; 374 375 if (period!=old_period) 376 { 377 synchronized(this) 378 { 379 _scavengePeriodMs=period; 380 if (_scavenger!=null) 381 _scavenger.interrupt(); 382 } 383 } 384 } 385 386 387 390 public boolean getHttpOnly() 391 { 392 return _httpOnly; 393 } 394 395 396 399 public void setHttpOnly(boolean httpOnly) 400 { 401 _httpOnly = httpOnly; 402 } 403 404 405 408 public boolean getSecureCookies() 409 { 410 return _secureCookies; 411 } 412 413 414 417 public void setSecureCookies(boolean secureCookies) 418 { 419 _secureCookies = secureCookies; 420 } 421 422 423 public boolean isInvalidateGlobal() 424 { 425 return _invalidateGlobal; 426 } 427 428 429 434 public void setInvalidateGlobal(boolean global) 435 { 436 _invalidateGlobal=global; 437 } 438 439 440 public void addEventListener(EventListener listener) 441 throws IllegalArgumentException 442 { 443 444 if (listener instanceof HttpSessionAttributeListener ) 445 _sessionAttributeListeners.add(listener); 446 if (listener instanceof HttpSessionListener ) 447 _sessionListeners.add(listener); 448 } 449 450 451 public void removeEventListener(EventListener listener) 452 { 453 if (listener instanceof HttpSessionAttributeListener ) 454 _sessionAttributeListeners.remove(listener); 455 if (listener instanceof HttpSessionListener ) 456 _sessionListeners.remove(listener); 457 } 458 459 460 public boolean isStarted() 461 { 462 return _scavenger!=null; 463 } 464 465 466 public void start() 467 throws Exception 468 { 469 if (_random==null) 470 { 471 log.debug("New random session seed"); 472 _random=new Random (); 473 } 474 else 475 if(log.isDebugEnabled())log.debug("Initializing random session key: "+_random); 476 _random.nextLong(); 477 478 if (_sessions==null) 479 _sessions=new HashMap (); 480 481 if (_scavenger == null) 483 { 484 _scavenger = new SessionScavenger(); 485 _scavenger.start(); 486 } 487 } 488 489 490 491 public void stop() 492 { 493 ArrayList sessions = new ArrayList (_sessions.values()); 495 for (Iterator i = sessions.iterator(); i.hasNext(); ) 496 { 497 Session session = (Session )i.next(); 498 session.invalidate(); 499 } 500 _sessions.clear(); 501 502 SessionScavenger scavenger = _scavenger; 504 _scavenger=null; 505 if (scavenger!=null) 506 scavenger.interrupt(); 507 } 508 509 510 513 private void scavenge() 514 { 515 Thread thread = Thread.currentThread(); 516 ClassLoader old_loader = thread.getContextClassLoader(); 517 try 518 { 519 if (_handler==null) 520 return; 521 522 ClassLoader loader = _handler.getClassLoader(); 523 if (loader!=null) 524 thread.setContextClassLoader(loader); 525 526 long now = System.currentTimeMillis(); 527 528 Object stale=null; 531 532 533 synchronized(AbstractSessionManager.this) 534 { 535 for (Iterator i = _sessions.values().iterator(); i.hasNext(); ) 537 { 538 Session session = (Session )i.next(); 539 long idleTime = session._maxIdleMs; 540 if (idleTime > 0 && session._accessed + idleTime < now) { 541 stale=LazyList.add(stale,session); 543 } 544 } 545 } 546 547 for (int i = LazyList.size(stale); i-->0;) 549 { 550 Session session=(Session )LazyList.get(stale,i); 552 long idleTime = session._maxIdleMs; 553 if (idleTime > 0 && session._accessed + idleTime < System.currentTimeMillis()) 554 { 555 session.invalidate(); 556 int nbsess = this._sessions.size(); 557 if (nbsess < this._minSessions) 558 this._minSessions = nbsess; 559 } 560 } 561 } 562 finally 563 { 564 thread.setContextClassLoader(old_loader); 565 } 566 } 567 568 569 570 571 572 573 class SessionScavenger extends Thread 574 { 575 public void run() 576 { 577 int period=-1; 578 try{ 579 while (isStarted()) 580 { 581 try { 582 if (period!=_scavengePeriodMs) 583 { 584 if(log.isDebugEnabled())log.debug("Session scavenger period = "+_scavengePeriodMs/1000+"s"); 585 period=_scavengePeriodMs; 586 } 587 sleep(period>1000?period:1000); 588 AbstractSessionManager.this.scavenge(); 589 } 590 catch (InterruptedException ex){continue;} 591 catch (Error e) {log.warn(LogSupport.EXCEPTION,e);} 592 catch (Exception e) {log.warn(LogSupport.EXCEPTION,e);} 593 } 594 } 595 finally 596 { 597 AbstractSessionManager.this._scavenger=null; 598 log.debug("Session scavenger exited"); 599 } 600 } 601 602 SessionScavenger() 603 { 604 super("SessionScavenger"); 605 setDaemon(true); 606 } 607 608 } 610 611 612 613 614 615 public abstract class Session implements SessionManager.Session 616 { 617 Map _values; 618 boolean _invalid=false; 619 boolean _newSession=true; 620 long _created=System.currentTimeMillis(); 621 long _accessed=_created; 622 long _maxIdleMs = _dftMaxIdleSecs*1000; 623 String _id; 624 625 626 protected Session(HttpServletRequest request) 627 { 628 _id=newSessionId(request,_created); 629 if (_dftMaxIdleSecs>=0) 630 _maxIdleMs=_dftMaxIdleSecs*1000; 631 } 632 633 634 protected abstract Map newAttributeMap(); 635 636 637 public void access() 638 { 639 _newSession=false; 640 _accessed=System.currentTimeMillis(); 641 } 642 643 644 public boolean isValid() 645 { 646 return !_invalid; 647 } 648 649 650 public ServletContext getServletContext() 651 { 652 return _handler.getServletContext(); 653 } 654 655 656 public String getId() 657 throws IllegalStateException 658 { 659 return _id; 660 } 661 662 663 public long getCreationTime() 664 throws IllegalStateException 665 { 666 if (_invalid) throw new IllegalStateException (); 667 return _created; 668 } 669 670 671 public long getLastAccessedTime() 672 throws IllegalStateException 673 { 674 if (_invalid) throw new IllegalStateException (); 675 return _accessed; 676 } 677 678 679 public int getMaxInactiveInterval() 680 { 681 if (_invalid) throw new IllegalStateException (); 682 return (int)(_maxIdleMs / 1000); 683 } 684 685 686 689 public HttpSessionContext getSessionContext() 690 throws IllegalStateException 691 { 692 if (_invalid) throw new IllegalStateException (); 693 return SessionContext.NULL_IMPL; 694 } 695 696 697 public void setMaxInactiveInterval(int secs) 698 { 699 _maxIdleMs = (long)secs * 1000; 700 if (_maxIdleMs>0 && (_maxIdleMs/10)<_scavengePeriodMs) 701 AbstractSessionManager.this.setScavengePeriod((secs+9)/10); 702 } 703 704 705 public void invalidate() throws IllegalStateException 706 { 707 if (log.isDebugEnabled()) log.debug("Invalidate session "+getId()+" in "+_handler.getHttpContext()); 708 try 709 { 710 synchronized (this) 712 { 713 if (_invalid) 714 throw new IllegalStateException (); 715 716 if (_sessionListeners!=null) 717 { 718 HttpSessionEvent event=new HttpSessionEvent (this); 719 for (int i=_sessionListeners.size(); i-->0;) 720 ((HttpSessionListener )_sessionListeners.get(i)).sessionDestroyed(event); 721 } 722 723 if (_values!=null) 724 { 725 Iterator iter=_values.keySet().iterator(); 726 while (iter.hasNext()) 727 { 728 String key=(String )iter.next(); 729 Object value=_values.get(key); 730 iter.remove(); 731 unbindValue(key,value); 732 733 if (_sessionAttributeListeners.size()>0) 734 { 735 HttpSessionBindingEvent event=new HttpSessionBindingEvent (this,key,value); 736 737 for (int i=0; i<_sessionAttributeListeners.size(); i++) 738 { 739 ((HttpSessionAttributeListener )_sessionAttributeListeners.get(i)).attributeRemoved(event); 740 } 741 } 742 } 743 } 744 } 745 } 746 finally 747 { 748 synchronized (__allSessions) 750 { 751 synchronized (_sessions) 752 { 753 _invalid=true; 754 _sessions.remove(getId()); 755 __allSessions.removeValue(getId(), this); 756 757 if (isInvalidateGlobal()) 758 { 759 while(__allSessions.containsKey(getId())) 761 { 762 Session session=(Session )__allSessions.getValue(getId(),0); 763 session.invalidate(); 764 } 765 } 766 } 767 } 768 } 769 } 770 771 772 public boolean isNew() 773 throws IllegalStateException 774 { 775 if (_invalid) throw new IllegalStateException (); 776 return _newSession; 777 } 778 779 780 781 public synchronized Object getAttribute(String name) 782 { 783 if (_invalid) throw new IllegalStateException (); 784 if (_values==null) 785 return null; 786 return _values.get(name); 787 } 788 789 790 public synchronized Enumeration getAttributeNames() 791 { 792 if (_invalid) throw new IllegalStateException (); 793 List names = _values==null?Collections.EMPTY_LIST:new ArrayList (_values.keySet()); 794 return Collections.enumeration(names); 795 } 796 797 798 public synchronized void setAttribute(String name, Object value) 799 { 800 if (_invalid) throw new IllegalStateException (); 801 if (_values==null) 802 _values=newAttributeMap(); 803 Object oldValue = _values.put(name,value); 804 805 if (value==null || !value.equals(oldValue)) 806 { 807 unbindValue(name, oldValue); 808 bindValue(name, value); 809 810 if (_sessionAttributeListeners.size()>0) 811 { 812 HttpSessionBindingEvent event = 813 new HttpSessionBindingEvent (this,name, 814 oldValue==null?value:oldValue); 815 816 for(int i=0;i<_sessionAttributeListeners.size();i++) 817 { 818 HttpSessionAttributeListener l = 819 (HttpSessionAttributeListener ) 820 _sessionAttributeListeners.get(i); 821 822 if (oldValue==null) 823 l.attributeAdded(event); 824 else if (value==null) 825 l.attributeRemoved(event); 826 else 827 l.attributeReplaced(event); 828 } 829 } 830 } 831 } 832 833 834 public synchronized void removeAttribute(String name) 835 { 836 if (_invalid) throw new IllegalStateException (); 837 if (_values==null) 838 return; 839 840 Object old=_values.remove(name); 841 if (old!=null) 842 { 843 unbindValue(name, old); 844 if (_sessionAttributeListeners.size()>0) 845 { 846 HttpSessionBindingEvent event = 847 new HttpSessionBindingEvent (this,name,old); 848 849 for(int i=0;i<_sessionAttributeListeners.size();i++) 850 { 851 HttpSessionAttributeListener l = 852 (HttpSessionAttributeListener ) 853 _sessionAttributeListeners.get(i); 854 l.attributeRemoved(event); 855 } 856 } 857 } 858 } 859 860 861 865 public Object getValue(String name) 866 throws IllegalStateException 867 { 868 return getAttribute(name); 869 } 870 871 872 876 public synchronized String [] getValueNames() 877 throws IllegalStateException 878 { 879 if (_invalid) throw new IllegalStateException (); 880 if (_values==null) 881 return new String [0]; 882 String [] a = new String [_values.size()]; 883 return (String [])_values.keySet().toArray(a); 884 } 885 886 887 891 public void putValue(java.lang.String name, 892 java.lang.Object value) 893 throws IllegalStateException 894 { 895 setAttribute(name,value); 896 } 897 898 899 903 public void removeValue(java.lang.String name) 904 throws IllegalStateException 905 { 906 removeAttribute(name); 907 } 908 909 910 911 private void bindValue(java.lang.String name, Object value) 912 { 913 if (value!=null && value instanceof HttpSessionBindingListener ) 914 ((HttpSessionBindingListener )value) 915 .valueBound(new HttpSessionBindingEvent (this,name)); 916 } 917 918 919 920 private void unbindValue(java.lang.String name, Object value) 921 { 922 if (value!=null && value instanceof HttpSessionBindingListener ) 923 ((HttpSessionBindingListener )value) 924 .valueUnbound(new HttpSessionBindingEvent (this,name)); 925 } 926 } 927 928 929 } 930 | Popular Tags |