1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18
19 package org.apache.catalina.session;
20
21 import java.beans.PropertyChangeEvent;
22 import java.beans.PropertyChangeListener;
23 import java.io.IOException;
24 import java.security.AccessController;
25 import java.security.PrivilegedActionException;
26 import java.security.PrivilegedExceptionAction;
27 import org.apache.catalina.Container;
28 import org.apache.catalina.Context;
29 import org.apache.catalina.Lifecycle;
30 import org.apache.catalina.LifecycleException;
31 import org.apache.catalina.LifecycleListener;
32 import org.apache.catalina.Session;
33 import org.apache.catalina.Store;
34 import org.apache.catalina.util.LifecycleSupport;
35
36 import org.apache.catalina.security.SecurityUtil;
37 import org.apache.juli.logging.Log;
38 import org.apache.juli.logging.LogFactory;
39 /**
40 * Extends the <b>ManagerBase</b> class to implement most of the
41 * functionality required by a Manager which supports any kind of
42 * persistence, even if onlyfor restarts.
43 * <p>
44 * <b>IMPLEMENTATION NOTE</b>: Correct behavior of session storing and
45 * reloading depends upon external calls to the <code>start()</code> and
46 * <code>stop()</code> methods of this class at the correct times.
47 *
48 * @author Craig R. McClanahan
49 * @author Jean-Francois Arcand
50 * @version $Revision: 467222 $ $Date: 2006-10-24 05:17:11 +0200 (mar., 24 oct. 2006) $
51 */
52
53 public abstract class PersistentManagerBase
54 extends ManagerBase
55 implements Lifecycle, PropertyChangeListener {
56
57 private static Log log = LogFactory.getLog(PersistentManagerBase.class);
58
59 // ---------------------------------------------------- Security Classes
60
61 private class PrivilegedStoreClear
62 implements PrivilegedExceptionAction {
63
64 PrivilegedStoreClear() {
65 }
66
67 public Object run() throws Exception{
68 store.clear();
69 return null;
70 }
71 }
72
73 private class PrivilegedStoreRemove
74 implements PrivilegedExceptionAction {
75
76 private String id;
77
78 PrivilegedStoreRemove(String id) {
79 this.id = id;
80 }
81
82 public Object run() throws Exception{
83 store.remove(id);
84 return null;
85 }
86 }
87
88 private class PrivilegedStoreLoad
89 implements PrivilegedExceptionAction {
90
91 private String id;
92
93 PrivilegedStoreLoad(String id) {
94 this.id = id;
95 }
96
97 public Object run() throws Exception{
98 return store.load(id);
99 }
100 }
101
102 private class PrivilegedStoreSave
103 implements PrivilegedExceptionAction {
104
105 private Session session;
106
107 PrivilegedStoreSave(Session session) {
108 this.session = session;
109 }
110
111 public Object run() throws Exception{
112 store.save(session);
113 return null;
114 }
115 }
116
117 private class PrivilegedStoreKeys
118 implements PrivilegedExceptionAction {
119
120 PrivilegedStoreKeys() {
121 }
122
123 public Object run() throws Exception{
124 return store.keys();
125 }
126 }
127
128 // ----------------------------------------------------- Instance Variables
129
130
131 /**
132 * The descriptive information about this implementation.
133 */
134 private static final String info = "PersistentManagerBase/1.1";
135
136
137 /**
138 * The lifecycle event support for this component.
139 */
140 protected LifecycleSupport lifecycle = new LifecycleSupport(this);
141
142
143 /**
144 * The maximum number of active Sessions allowed, or -1 for no limit.
145 */
146 protected int maxActiveSessions = -1;
147
148
149 /**
150 * The descriptive name of this Manager implementation (for logging).
151 */
152 private static String name = "PersistentManagerBase";
153
154
155 /**
156 * Has this component been started yet?
157 */
158 protected boolean started = false;
159
160
161 /**
162 * Store object which will manage the Session store.
163 */
164 protected Store store = null;
165
166
167 /**
168 * Whether to save and reload sessions when the Manager <code>unload</code>
169 * and <code>load</code> methods are called.
170 */
171 protected boolean saveOnRestart = true;
172
173
174 /**
175 * How long a session must be idle before it should be backed up.
176 * -1 means sessions won't be backed up.
177 */
178 protected int maxIdleBackup = -1;
179
180
181 /**
182 * Minimum time a session must be idle before it is swapped to disk.
183 * This overrides maxActiveSessions, to prevent thrashing if there are lots
184 * of active sessions. Setting to -1 means it's ignored.
185 */
186 protected int minIdleSwap = -1;
187
188 /**
189 * The maximum time a session may be idle before it should be swapped
190 * to file just on general principle. Setting this to -1 means sessions
191 * should not be forced out.
192 */
193 protected int maxIdleSwap = -1;
194
195
196 /**
197 * Number of session creations that failed due to maxActiveSessions.
198 */
199 protected int rejectedSessions = 0;
200
201
202 /**
203 * Processing time during session expiration and passivation.
204 */
205 protected long processingTime = 0;
206
207
208 // ------------------------------------------------------------- Properties
209
210
211
212
213
214 /**
215 * Indicates how many seconds old a session can get, after its last use in a
216 * request, before it should be backed up to the store. -1 means sessions
217 * are not backed up.
218 */
219 public int getMaxIdleBackup() {
220
221 return maxIdleBackup;
222
223 }
224
225
226 /**
227 * Sets the option to back sessions up to the Store after they
228 * are used in a request. Sessions remain available in memory
229 * after being backed up, so they are not passivated as they are
230 * when swapped out. The value set indicates how old a session
231 * may get (since its last use) before it must be backed up: -1
232 * means sessions are not backed up.
233 * <p>
234 * Note that this is not a hard limit: sessions are checked
235 * against this age limit periodically according to <b>processExpiresFrequency</b>.
236 * This value should be considered to indicate when a session is
237 * ripe for backing up.
238 * <p>
239 * So it is possible that a session may be idle for maxIdleBackup +
240 * processExpiresFrequency * engine.backgroundProcessorDelay seconds, plus the time it takes to handle other
241 * session expiration, swapping, etc. tasks.
242 *
243 * @param backup The number of seconds after their last accessed
244 * time when they should be written to the Store.
245 */
246 public void setMaxIdleBackup (int backup) {
247
248 if (backup == this.maxIdleBackup)
249 return;
250 int oldBackup = this.maxIdleBackup;
251 this.maxIdleBackup = backup;
252 support.firePropertyChange("maxIdleBackup",
253 new Integer(oldBackup),
254 new Integer(this.maxIdleBackup));
255
256 }
257
258
259 /**
260 * The time in seconds after which a session should be swapped out of
261 * memory to disk.
262 */
263 public int getMaxIdleSwap() {
264
265 return maxIdleSwap;
266
267 }
268
269
270 /**
271 * Sets the time in seconds after which a session should be swapped out of
272 * memory to disk.
273 */
274 public void setMaxIdleSwap(int max) {
275
276 if (max == this.maxIdleSwap)
277 return;
278 int oldMaxIdleSwap = this.maxIdleSwap;
279 this.maxIdleSwap = max;
280 support.firePropertyChange("maxIdleSwap",
281 new Integer(oldMaxIdleSwap),
282 new Integer(this.maxIdleSwap));
283
284 }
285
286
287 /**
288 * The minimum time in seconds that a session must be idle before
289 * it can be swapped out of memory, or -1 if it can be swapped out
290 * at any time.
291 */
292 public int getMinIdleSwap() {
293
294 return minIdleSwap;
295
296 }
297
298
299 /**
300 * Sets the minimum time in seconds that a session must be idle before
301 * it can be swapped out of memory due to maxActiveSession. Set it to -1
302 * if it can be swapped out at any time.
303 */
304 public void setMinIdleSwap(int min) {
305
306 if (this.minIdleSwap == min)
307 return;
308 int oldMinIdleSwap = this.minIdleSwap;
309 this.minIdleSwap = min;
310 support.firePropertyChange("minIdleSwap",
311 new Integer(oldMinIdleSwap),
312 new Integer(this.minIdleSwap));
313
314 }
315
316
317 /**
318 * Set the Container with which this Manager has been associated. If it is a
319 * Context (the usual case), listen for changes to the session timeout
320 * property.
321 *
322 * @param container
323 * The associated Container
324 */
325 public void setContainer(Container container) {
326
327 // De-register from the old Container (if any)
328 if ((this.container != null) && (this.container instanceof Context))
329 ((Context) this.container).removePropertyChangeListener(this);
330
331 // Default processing provided by our superclass
332 super.setContainer(container);
333
334 // Register with the new Container (if any)
335 if ((this.container != null) && (this.container instanceof Context)) {
336 setMaxInactiveInterval
337 ( ((Context) this.container).getSessionTimeout()*60 );
338 ((Context) this.container).addPropertyChangeListener(this);
339 }
340
341 }
342
343
344 /**
345 * Return descriptive information about this Manager implementation and
346 * the corresponding version number, in the format
347 * <code><description>/<version></code>.
348 */
349 public String getInfo() {
350
351 return (info);
352
353 }
354
355
356 /**
357 * Return true, if the session id is loaded in memory
358 * otherwise false is returned
359 *
360 * @param id The session id for the session to be searched for
361 */
362 public boolean isLoaded( String id ){
363 try {
364 if ( super.findSession(id) != null )
365 return true;
366 } catch (IOException e) {
367 log.error("checking isLoaded for id, " + id + ", "+e.getMessage(), e);
368 }
369 return false;
370 }
371
372
373 /**
374 * Return the maximum number of active Sessions allowed, or -1 for
375 * no limit.
376 */
377 public int getMaxActiveSessions() {
378
379 return (this.maxActiveSessions);
380
381 }
382
383
384 /**
385 * Set the maximum number of actives Sessions allowed, or -1 for
386 * no limit.
387 *
388 * @param max The new maximum number of sessions
389 */
390 public void setMaxActiveSessions(int max) {
391
392 int oldMaxActiveSessions = this.maxActiveSessions;
393 this.maxActiveSessions = max;
394 support.firePropertyChange("maxActiveSessions",
395 new Integer(oldMaxActiveSessions),
396 new Integer(this.maxActiveSessions));
397
398 }
399
400
401 /**
402 * Number of session creations that failed due to maxActiveSessions.
403 *
404 * @return The count
405 */
406 public int getRejectedSessions() {
407 return rejectedSessions;
408 }
409
410
411 public void setRejectedSessions(int rejectedSessions) {
412 this.rejectedSessions = rejectedSessions;
413 }
414
415 /**
416 * Return the descriptive short name of this Manager implementation.
417 */
418 public String getName() {
419
420 return (name);
421
422 }
423
424
425 /**
426 * Get the started status.
427 */
428 protected boolean isStarted() {
429
430 return started;
431
432 }
433
434
435 /**
436 * Set the started flag
437 */
438 protected void setStarted(boolean started) {
439
440 this.started = started;
441
442 }
443
444
445 /**
446 * Set the Store object which will manage persistent Session
447 * storage for this Manager.
448 *
449 * @param store the associated Store
450 */
451 public void setStore(Store store) {
452 this.store = store;
453 store.setManager(this);
454
455 }
456
457
458 /**
459 * Return the Store object which manages persistent Session
460 * storage for this Manager.
461 */
462 public Store getStore() {
463
464 return (this.store);
465
466 }
467
468
469
470 /**
471 * Indicates whether sessions are saved when the Manager is shut down
472 * properly. This requires the unload() method to be called.
473 */
474 public boolean getSaveOnRestart() {
475
476 return saveOnRestart;
477
478 }
479
480
481 /**
482 * Set the option to save sessions to the Store when the Manager is
483 * shut down, then loaded when the Manager starts again. If set to
484 * false, any sessions found in the Store may still be picked up when
485 * the Manager is started again.
486 *
487 * @param saveOnRestart true if sessions should be saved on restart, false if
488 * they should be ignored.
489 */
490 public void setSaveOnRestart(boolean saveOnRestart) {
491
492 if (saveOnRestart == this.saveOnRestart)
493 return;
494
495 boolean oldSaveOnRestart = this.saveOnRestart;
496 this.saveOnRestart = saveOnRestart;
497 support.firePropertyChange("saveOnRestart",
498 new Boolean(oldSaveOnRestart),
499 new Boolean(this.saveOnRestart));
500
501 }
502
503
504 // --------------------------------------------------------- Public Methods
505
506
507 /**
508 * Clear all sessions from the Store.
509 */
510 public void clearStore() {
511
512 if (store == null)
513 return;
514
515 try {
516 if (SecurityUtil.isPackageProtectionEnabled()){
517 try{
518 AccessController.doPrivileged(new PrivilegedStoreClear());
519 }catch(PrivilegedActionException ex){
520 Exception exception = ex.getException();
521 log.error("Exception clearing the Store: " + exception);
522 exception.printStackTrace();
523 }
524 } else {
525 store.clear();
526 }
527 } catch (IOException e) {
528 log.error("Exception clearing the Store: " + e);
529 e.printStackTrace();
530 }
531
532 }
533
534
535 /**
536 * Implements the Manager interface, direct call to processExpires and processPersistenceChecks
537 */
538 public void processExpires() {
539
540 long timeNow = System.currentTimeMillis();
541 Session sessions[] = findSessions();
542 int expireHere = 0 ;
543 if(log.isDebugEnabled())
544 log.debug("Start expire sessions " + getName() + " at " + timeNow + " sessioncount " + sessions.length);
545 for (int i = 0; i < sessions.length; i++) {
546 if (!sessions[i].isValid()) {
547 expiredSessions++;
548 expireHere++;
549 }
550 }
551 processPersistenceChecks();
552 if ((getStore() != null) && (getStore() instanceof StoreBase)) {
553 ((StoreBase) getStore()).processExpires();
554 }
555
556 long timeEnd = System.currentTimeMillis();
557 if(log.isDebugEnabled())
558 log.debug("End expire sessions " + getName() + " processingTime " + (timeEnd - timeNow) + " expired sessions: " + expireHere);
559 processingTime += (timeEnd - timeNow);
560
561 }
562
563
564 /**
565 * Called by the background thread after active sessions have been checked
566 * for expiration, to allow sessions to be swapped out, backed up, etc.
567 */
568 public void processPersistenceChecks() {
569
570 processMaxIdleSwaps();
571 processMaxActiveSwaps();
572 processMaxIdleBackups();
573
574 }
575
576
577 /**
578 * Return the active Session, associated with this Manager, with the
579 * specified session id (if any); otherwise return <code>null</code>.
580 * This method checks the persistence store if persistence is enabled,
581 * otherwise just uses the functionality from ManagerBase.
582 *
583 * @param id The session id for the session to be returned
584 *
585 * @exception IllegalStateException if a new session cannot be
586 * instantiated for any reason
587 * @exception IOException if an input/output error occurs while
588 * processing this request
589 */
590 public Session findSession(String id) throws IOException {
591
592 Session session = super.findSession(id);
593 if (session != null)
594 return (session);
595
596 // See if the Session is in the Store
597 session = swapIn(id);
598 return (session);
599
600 }
601
602 /**
603 * Remove this Session from the active Sessions for this Manager,
604 * but not from the Store. (Used by the PersistentValve)
605 *
606 * @param session Session to be removed
607 */
608 public void removeSuper(Session session) {
609 super.remove (session);
610 }
611
612 /**
613 * Load all sessions found in the persistence mechanism, assuming
614 * they are marked as valid and have not passed their expiration
615 * limit. If persistence is not supported, this method returns
616 * without doing anything.
617 * <p>
618 * Note that by default, this method is not called by the MiddleManager
619 * class. In order to use it, a subclass must specifically call it,
620 * for example in the start() and/or processPersistenceChecks() methods.
621 */
622 public void load() {
623
624 // Initialize our internal data structures
625 sessions.clear();
626
627 if (store == null)
628 return;
629
630 String[] ids = null;
631 try {
632 if (SecurityUtil.isPackageProtectionEnabled()){
633 try{
634 ids = (String[])
635 AccessController.doPrivileged(new PrivilegedStoreKeys());
636 }catch(PrivilegedActionException ex){
637 Exception exception = ex.getException();
638 log.error("Exception in the Store during load: "
639 + exception);
640 exception.printStackTrace();
641 }
642 } else {
643 ids = store.keys();
644 }
645 } catch (IOException e) {
646 log.error("Can't load sessions from store, " + e.getMessage(), e);
647 return;
648 }
649
650 int n = ids.length;
651 if (n == 0)
652 return;
653
654 if (log.isDebugEnabled())
655 log.debug(sm.getString("persistentManager.loading", String.valueOf(n)));
656
657 for (int i = 0; i < n; i++)
658 try {
659 swapIn(ids[i]);
660 } catch (IOException e) {
661 log.error("Failed load session from store, " + e.getMessage(), e);
662 }
663
664 }
665
666
667 /**
668 * Remove this Session from the active Sessions for this Manager,
669 * and from the Store.
670 *
671 * @param session Session to be removed
672 */
673 public void remove(Session session) {
674
675 super.remove (session);
676
677 if (store != null){
678 removeSession(session.getIdInternal());
679 }
680 }
681
682
683 /**
684 * Remove this Session from the active Sessions for this Manager,
685 * and from the Store.
686 *
687 * @param id Session's id to be removed
688 */
689 protected void removeSession(String id){
690 try {
691 if (SecurityUtil.isPackageProtectionEnabled()){
692 try{
693 AccessController.doPrivileged(new PrivilegedStoreRemove(id));
694 }catch(PrivilegedActionException ex){
695 Exception exception = ex.getException();
696 log.error("Exception in the Store during removeSession: "
697 + exception);
698 exception.printStackTrace();
699 }
700 } else {
701 store.remove(id);
702 }
703 } catch (IOException e) {
704 log.error("Exception removing session " + e.getMessage());
705 e.printStackTrace();
706 }
707 }
708
709 /**
710 * Save all currently active sessions in the appropriate persistence
711 * mechanism, if any. If persistence is not supported, this method
712 * returns without doing anything.
713 * <p>
714 * Note that by default, this method is not called by the MiddleManager
715 * class. In order to use it, a subclass must specifically call it,
716 * for example in the stop() and/or processPersistenceChecks() methods.
717 */
718 public void unload() {
719
720 if (store == null)
721 return;
722
723 Session sessions[] = findSessions();
724 int n = sessions.length;
725 if (n == 0)
726 return;
727
728 if (log.isDebugEnabled())
729 log.debug(sm.getString("persistentManager.unloading",
730 String.valueOf(n)));
731
732 for (int i = 0; i < n; i++)
733 try {
734 swapOut(sessions[i]);
735 } catch (IOException e) {
736 ; // This is logged in writeSession()
737 }
738
739 }
740
741
742 // ------------------------------------------------------ Protected Methods
743
744
745 /**
746 * Look for a session in the Store and, if found, restore
747 * it in the Manager's list of active sessions if appropriate.
748 * The session will be removed from the Store after swapping
749 * in, but will not be added to the active session list if it
750 * is invalid or past its expiration.
751 */
752 protected Session swapIn(String id) throws IOException {
753
754 if (store == null)
755 return null;
756
757 Session session = null;
758 try {
759 if (SecurityUtil.isPackageProtectionEnabled()){
760 try{
761 session = (Session)
762 AccessController.doPrivileged(new PrivilegedStoreLoad(id));
763 }catch(PrivilegedActionException ex){
764 Exception exception = ex.getException();
765 log.error("Exception in the Store during swapIn: "
766 + exception);
767 if (exception instanceof IOException){
768 throw (IOException)exception;
769 } else if (exception instanceof ClassNotFoundException) {
770 throw (ClassNotFoundException)exception;
771 }
772 }
773 } else {
774 session = store.load(id);
775 }
776 } catch (ClassNotFoundException e) {
777 log.error(sm.getString("persistentManager.deserializeError", id, e));
778 throw new IllegalStateException
779 (sm.getString("persistentManager.deserializeError", id, e));
780 }
781
782 if (session == null)
783 return (null);
784
785 if (!session.isValid()) {
786 log.error("session swapped in is invalid or expired");
787 session.expire();
788 removeSession(id);
789 return (null);
790 }
791
792 if(log.isDebugEnabled())
793 log.debug(sm.getString("persistentManager.swapIn", id));
794
795 session.setManager(this);
796 // make sure the listeners know about it.
797 ((StandardSession)session).tellNew();
798 add(session);
799 ((StandardSession)session).activate();
800 session.endAccess();
801
802 return (session);
803
804 }
805
806
807 /**
808 * Remove the session from the Manager's list of active
809 * sessions and write it out to the Store. If the session
810 * is past its expiration or invalid, this method does
811 * nothing.
812 *
813 * @param session The Session to write out.
814 */
815 protected void swapOut(Session session) throws IOException {
816
817 if (store == null || !session.isValid()) {
818 return;
819 }
820
821 ((StandardSession)session).passivate();
822 writeSession(session);
823 super.remove(session);
824 session.recycle();
825
826 }
827
828
829 /**
830 * Write the provided session to the Store without modifying
831 * the copy in memory or triggering passivation events. Does
832 * nothing if the session is invalid or past its expiration.
833 */
834 protected void writeSession(Session session) throws IOException {
835
836 if (store == null || !session.isValid()) {
837 return;
838 }
839
840 try {
841 if (SecurityUtil.isPackageProtectionEnabled()){
842 try{
843 AccessController.doPrivileged(new PrivilegedStoreSave(session));
844 }catch(PrivilegedActionException ex){
845 Exception exception = ex.getException();
846 log.error("Exception in the Store during writeSession: "
847 + exception);
848 exception.printStackTrace();
849 }
850 } else {
851 store.save(session);
852 }
853 } catch (IOException e) {
854 log.error(sm.getString
855 ("persistentManager.serializeError", session.getIdInternal(), e));
856 throw e;
857 }
858
859 }
860
861
862 // ------------------------------------------------------ Lifecycle Methods
863
864
865 /**
866 * Add a lifecycle event listener to this component.
867 *
868 * @param listener The listener to add
869 */
870 public void addLifecycleListener(LifecycleListener listener) {
871
872 lifecycle.addLifecycleListener(listener);
873
874 }
875
876
877 /**
878 * Get the lifecycle listeners associated with this lifecycle. If this
879 * Lifecycle has no listeners registered, a zero-length array is returned.
880 */
881 public LifecycleListener[] findLifecycleListeners() {
882
883 return lifecycle.findLifecycleListeners();
884
885 }
886
887
888 /**
889 * Remove a lifecycle event listener from this component.
890 *
891 * @param listener The listener to remove
892 */
893 public void removeLifecycleListener(LifecycleListener listener) {
894
895 lifecycle.removeLifecycleListener(listener);
896
897 }
898
899
900 /**
901 * Prepare for the beginning of active use of the public methods of this
902 * component. This method should be called after <code>configure()</code>,
903 * and before any of the public methods of the component are utilized.
904 *
905 * @exception LifecycleException if this component detects a fatal error
906 * that prevents this component from being used
907 */
908 public void start() throws LifecycleException {
909
910 // Validate and update our current component state
911 if (started) {
912 log.info(sm.getString("standardManager.alreadyStarted"));
913 return;
914 }
915 if( ! initialized )
916 init();
917
918 lifecycle.fireLifecycleEvent(START_EVENT, null);
919 started = true;
920
921 // Force initialization of the random number generator
922 if (log.isDebugEnabled())
923 log.debug("Force random number initialization starting");
924 String dummy = generateSessionId();
925 if (log.isDebugEnabled())
926 log.debug("Force random number initialization completed");
927
928 if (store == null)
929 log.error("No Store configured, persistence disabled");
930 else if (store instanceof Lifecycle)
931 ((Lifecycle)store).start();
932
933 }
934
935
936 /**
937 * Gracefully terminate the active use of the public methods of this
938 * component. This method should be the last one called on a given
939 * instance of this component.
940 *
941 * @exception LifecycleException if this component detects a fatal error
942 * that needs to be reported
943 */
944 public void stop() throws LifecycleException {
945
946 if (log.isDebugEnabled())
947 log.debug("Stopping");
948
949 // Validate and update our current component state
950 if (!isStarted()) {
951 log.info(sm.getString("standardManager.notStarted"));
952 return;
953 }
954
955 lifecycle.fireLifecycleEvent(STOP_EVENT, null);
956 setStarted(false);
957
958 if (getStore() != null && saveOnRestart) {
959 unload();
960 } else {
961 // Expire all active sessions
962 Session sessions[] = findSessions();
963 for (int i = 0; i < sessions.length; i++) {
964 StandardSession session = (StandardSession) sessions[i];
965 if (!session.isValid())
966 continue;
967 session.expire();
968 }
969 }
970
971 if (getStore() != null && getStore() instanceof Lifecycle)
972 ((Lifecycle)getStore()).stop();
973
974 // Require a new random number generator if we are restarted
975 this.random = null;
976
977 if( initialized )
978 destroy();
979
980 }
981
982
983 // ----------------------------------------- PropertyChangeListener Methods
984
985
986 /**
987 * Process property change events from our associated Context.
988 *
989 * @param event The property change event that has occurred
990 */
991 public void propertyChange(PropertyChangeEvent event) {
992
993 // Validate the source of this event
994 if (!(event.getSource() instanceof Context))
995 return;
996 Context context = (Context) event.getSource();
997
998 // Process a relevant property change
999 if (event.getPropertyName().equals("sessionTimeout")) {
1000 try {
1001 setMaxInactiveInterval
1002 ( ((Integer) event.getNewValue()).intValue()*60 );
1003 } catch (NumberFormatException e) {
1004 log.error(sm.getString("standardManager.sessionTimeout",
1005 event.getNewValue().toString()));
1006 }
1007 }
1008
1009 }
1010
1011
1012 // ------------------------------------------------------ Protected Methods
1013
1014
1015 /**
1016 * Swap idle sessions out to Store if they are idle too long.
1017 */
1018 protected void processMaxIdleSwaps() {
1019
1020 if (!isStarted() || maxIdleSwap < 0)
1021 return;
1022
1023 Session sessions[] = findSessions();
1024 long timeNow = System.currentTimeMillis();
1025
1026 // Swap out all sessions idle longer than maxIdleSwap
1027 // FIXME: What's preventing us from mangling a session during
1028 // a request?
1029 if (maxIdleSwap >= 0) {
1030 for (int i = 0; i < sessions.length; i++) {
1031 StandardSession session = (StandardSession) sessions[i];
1032 if (!session.isValid())
1033 continue;
1034 int timeIdle = // Truncate, do not round up
1035 (int) ((timeNow - session.getLastAccessedTime()) / 1000L);
1036 if (timeIdle > maxIdleSwap && timeIdle > minIdleSwap) {
1037 if (log.isDebugEnabled())
1038 log.debug(sm.getString
1039 ("persistentManager.swapMaxIdle",
1040 session.getIdInternal(), new Integer(timeIdle)));
1041 try {
1042 swapOut(session);
1043 } catch (IOException e) {
1044 ; // This is logged in writeSession()
1045 }
1046 }
1047 }
1048 }
1049
1050 }
1051
1052
1053 /**
1054 * Swap idle sessions out to Store if too many are active
1055 */
1056 protected void processMaxActiveSwaps() {
1057
1058 if (!isStarted() || getMaxActiveSessions() < 0)
1059 return;
1060
1061 Session sessions[] = findSessions();
1062
1063 // FIXME: Smarter algorithm (LRU)
1064 if (getMaxActiveSessions() >= sessions.length)
1065 return;
1066
1067 if(log.isDebugEnabled())
1068 log.debug(sm.getString
1069 ("persistentManager.tooManyActive",
1070 new Integer(sessions.length)));
1071
1072 int toswap = sessions.length - getMaxActiveSessions();
1073 long timeNow = System.currentTimeMillis();
1074
1075 for (int i = 0; i < sessions.length && toswap > 0; i++) {
1076 int timeIdle = // Truncate, do not round up
1077 (int) ((timeNow - sessions[i].getLastAccessedTime()) / 1000L);
1078 if (timeIdle > minIdleSwap) {
1079 if(log.isDebugEnabled())
1080 log.debug(sm.getString
1081 ("persistentManager.swapTooManyActive",
1082 sessions[i].getIdInternal(), new Integer(timeIdle)));
1083 try {
1084 swapOut(sessions[i]);
1085 } catch (IOException e) {
1086 ; // This is logged in writeSession()
1087 }
1088 toswap--;
1089 }
1090 }
1091
1092 }
1093
1094
1095 /**
1096 * Back up idle sessions.
1097 */
1098 protected void processMaxIdleBackups() {
1099
1100 if (!isStarted() || maxIdleBackup < 0)
1101 return;
1102
1103 Session sessions[] = findSessions();
1104 long timeNow = System.currentTimeMillis();
1105
1106 // Back up all sessions idle longer than maxIdleBackup
1107 if (maxIdleBackup >= 0) {
1108 for (int i = 0; i < sessions.length; i++) {
1109 StandardSession session = (StandardSession) sessions[i];
1110 if (!session.isValid())
1111 continue;
1112 int timeIdle = // Truncate, do not round up
1113 (int) ((timeNow - session.getLastAccessedTime()) / 1000L);
1114 if (timeIdle > maxIdleBackup) {
1115 if (log.isDebugEnabled())
1116 log.debug(sm.getString
1117 ("persistentManager.backupMaxIdle",
1118 session.getIdInternal(), new Integer(timeIdle)));
1119
1120 try {
1121 writeSession(session);
1122 } catch (IOException e) {
1123 ; // This is logged in writeSession()
1124 }
1125 }
1126 }
1127 }
1128
1129 }
1130
1131 }