KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > catalina > session > StandardManager


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 JavaDoc;
22 import java.beans.PropertyChangeListener JavaDoc;
23 import java.io.BufferedInputStream JavaDoc;
24 import java.io.BufferedOutputStream JavaDoc;
25 import java.io.File JavaDoc;
26 import java.io.FileInputStream JavaDoc;
27 import java.io.FileNotFoundException JavaDoc;
28 import java.io.FileOutputStream JavaDoc;
29 import java.io.IOException JavaDoc;
30 import java.io.ObjectInputStream JavaDoc;
31 import java.io.ObjectOutputStream JavaDoc;
32 import java.security.AccessController JavaDoc;
33 import java.security.PrivilegedActionException JavaDoc;
34 import java.security.PrivilegedExceptionAction JavaDoc;
35 import java.util.ArrayList JavaDoc;
36 import java.util.Iterator JavaDoc;
37 import javax.servlet.ServletContext JavaDoc;
38 import org.apache.catalina.Container;
39 import org.apache.catalina.Context;
40 import org.apache.catalina.Globals;
41 import org.apache.catalina.Lifecycle;
42 import org.apache.catalina.LifecycleException;
43 import org.apache.catalina.LifecycleListener;
44 import org.apache.catalina.Loader;
45 import org.apache.catalina.Session;
46 import org.apache.catalina.util.CustomObjectInputStream;
47 import org.apache.catalina.util.LifecycleSupport;
48
49 import org.apache.catalina.security.SecurityUtil;
50 /**
51  * Standard implementation of the <b>Manager</b> interface that provides
52  * simple session persistence across restarts of this component (such as
53  * when the entire server is shut down and restarted, or when a particular
54  * web application is reloaded.
55  * <p>
56  * <b>IMPLEMENTATION NOTE</b>: Correct behavior of session storing and
57  * reloading depends upon external calls to the <code>start()</code> and
58  * <code>stop()</code> methods of this class at the correct times.
59  *
60  * @author Craig R. McClanahan
61  * @author Jean-Francois Arcand
62  * @version $Revision: 467222 $ $Date: 2006-10-24 05:17:11 +0200 (mar., 24 oct. 2006) $
63  */

64
65 public class StandardManager
66     extends ManagerBase
67     implements Lifecycle, PropertyChangeListener JavaDoc {
68
69     // ---------------------------------------------------- Security Classes
70
private class PrivilegedDoLoad
71         implements PrivilegedExceptionAction JavaDoc {
72
73         PrivilegedDoLoad() {
74         }
75
76         public Object JavaDoc run() throws Exception JavaDoc{
77            doLoad();
78            return null;
79         }
80     }
81
82     private class PrivilegedDoUnload
83         implements PrivilegedExceptionAction JavaDoc {
84
85         PrivilegedDoUnload() {
86         }
87
88         public Object JavaDoc run() throws Exception JavaDoc{
89             doUnload();
90             return null;
91         }
92
93     }
94
95
96     // ----------------------------------------------------- Instance Variables
97

98
99     /**
100      * The descriptive information about this implementation.
101      */

102     protected static final String JavaDoc info = "StandardManager/1.0";
103
104
105     /**
106      * The lifecycle event support for this component.
107      */

108     protected LifecycleSupport lifecycle = new LifecycleSupport(this);
109
110
111     /**
112      * The maximum number of active Sessions allowed, or -1 for no limit.
113      */

114     protected int maxActiveSessions = -1;
115
116
117     /**
118      * The descriptive name of this Manager implementation (for logging).
119      */

120     protected static String JavaDoc name = "StandardManager";
121
122
123     /**
124      * Path name of the disk file in which active sessions are saved
125      * when we stop, and from which these sessions are loaded when we start.
126      * A <code>null</code> value indicates that no persistence is desired.
127      * If this pathname is relative, it will be resolved against the
128      * temporary working directory provided by our context, available via
129      * the <code>javax.servlet.context.tempdir</code> context attribute.
130      */

131     protected String JavaDoc pathname = "SESSIONS.ser";
132
133
134     /**
135      * Has this component been started yet?
136      */

137     protected boolean started = false;
138
139
140     /**
141      * Number of session creations that failed due to maxActiveSessions.
142      */

143     protected int rejectedSessions = 0;
144
145
146     /**
147      * Processing time during session expiration.
148      */

149     protected long processingTime = 0;
150
151
152     // ------------------------------------------------------------- Properties
153

154
155     /**
156      * Set the Container with which this Manager has been associated. If
157      * it is a Context (the usual case), listen for changes to the session
158      * timeout property.
159      *
160      * @param container The associated Container
161      */

162     public void setContainer(Container container) {
163
164         // De-register from the old Container (if any)
165
if ((this.container != null) && (this.container instanceof Context JavaDoc))
166             ((Context JavaDoc) this.container).removePropertyChangeListener(this);
167
168         // Default processing provided by our superclass
169
super.setContainer(container);
170
171         // Register with the new Container (if any)
172
if ((this.container != null) && (this.container instanceof Context JavaDoc)) {
173             setMaxInactiveInterval
174                 ( ((Context JavaDoc) this.container).getSessionTimeout()*60 );
175             ((Context JavaDoc) this.container).addPropertyChangeListener(this);
176         }
177
178     }
179
180
181     /**
182      * Return descriptive information about this Manager implementation and
183      * the corresponding version number, in the format
184      * <code>&lt;description&gt;/&lt;version&gt;</code>.
185      */

186     public String JavaDoc getInfo() {
187
188         return (info);
189
190     }
191
192
193     /**
194      * Return the maximum number of active Sessions allowed, or -1 for
195      * no limit.
196      */

197     public int getMaxActiveSessions() {
198
199         return (this.maxActiveSessions);
200
201     }
202
203
204     /** Number of session creations that failed due to maxActiveSessions
205      *
206      * @return The count
207      */

208     public int getRejectedSessions() {
209         return rejectedSessions;
210     }
211
212
213     public void setRejectedSessions(int rejectedSessions) {
214         this.rejectedSessions = rejectedSessions;
215     }
216
217
218     /**
219      * Set the maximum number of actives Sessions allowed, or -1 for
220      * no limit.
221      *
222      * @param max The new maximum number of sessions
223      */

224     public void setMaxActiveSessions(int max) {
225
226         int oldMaxActiveSessions = this.maxActiveSessions;
227         this.maxActiveSessions = max;
228         support.firePropertyChange("maxActiveSessions",
229                                    new Integer JavaDoc(oldMaxActiveSessions),
230                                    new Integer JavaDoc(this.maxActiveSessions));
231
232     }
233
234
235     /**
236      * Return the descriptive short name of this Manager implementation.
237      */

238     public String JavaDoc getName() {
239
240         return (name);
241
242     }
243
244
245     /**
246      * Return the session persistence pathname, if any.
247      */

248     public String JavaDoc getPathname() {
249
250         return (this.pathname);
251
252     }
253
254
255     /**
256      * Set the session persistence pathname to the specified value. If no
257      * persistence support is desired, set the pathname to <code>null</code>.
258      *
259      * @param pathname New session persistence pathname
260      */

261     public void setPathname(String JavaDoc pathname) {
262
263         String JavaDoc oldPathname = this.pathname;
264         this.pathname = pathname;
265         support.firePropertyChange("pathname", oldPathname, this.pathname);
266
267     }
268
269
270     // --------------------------------------------------------- Public Methods
271

272     /**
273      * Construct and return a new session object, based on the default
274      * settings specified by this Manager's properties. The session
275      * id will be assigned by this method, and available via the getId()
276      * method of the returned session. If a new session cannot be created
277      * for any reason, return <code>null</code>.
278      *
279      * @exception IllegalStateException if a new session cannot be
280      * instantiated for any reason
281      */

282     public Session createSession(String JavaDoc sessionId) {
283
284         if ((maxActiveSessions >= 0) &&
285             (sessions.size() >= maxActiveSessions)) {
286             rejectedSessions++;
287             throw new IllegalStateException JavaDoc
288                 (sm.getString("standardManager.createSession.ise"));
289         }
290
291         return (super.createSession(sessionId));
292
293     }
294
295
296     /**
297      * Load any currently active sessions that were previously unloaded
298      * to the appropriate persistence mechanism, if any. If persistence is not
299      * supported, this method returns without doing anything.
300      *
301      * @exception ClassNotFoundException if a serialized class cannot be
302      * found during the reload
303      * @exception IOException if an input/output error occurs
304      */

305     public void load() throws ClassNotFoundException JavaDoc, IOException JavaDoc {
306         if (SecurityUtil.isPackageProtectionEnabled()){
307             try{
308                 AccessController.doPrivileged( new PrivilegedDoLoad() );
309             } catch (PrivilegedActionException JavaDoc ex){
310                 Exception JavaDoc exception = ex.getException();
311                 if (exception instanceof ClassNotFoundException JavaDoc){
312                     throw (ClassNotFoundException JavaDoc)exception;
313                 } else if (exception instanceof IOException JavaDoc){
314                     throw (IOException JavaDoc)exception;
315                 }
316                 if (log.isDebugEnabled())
317                     log.debug("Unreported exception in load() "
318                         + exception);
319             }
320         } else {
321             doLoad();
322         }
323     }
324
325
326     /**
327      * Load any currently active sessions that were previously unloaded
328      * to the appropriate persistence mechanism, if any. If persistence is not
329      * supported, this method returns without doing anything.
330      *
331      * @exception ClassNotFoundException if a serialized class cannot be
332      * found during the reload
333      * @exception IOException if an input/output error occurs
334      */

335     protected void doLoad() throws ClassNotFoundException JavaDoc, IOException JavaDoc {
336         if (log.isDebugEnabled())
337             log.debug("Start: Loading persisted sessions");
338
339         // Initialize our internal data structures
340
sessions.clear();
341
342         // Open an input stream to the specified pathname, if any
343
File JavaDoc file = file();
344         if (file == null)
345             return;
346         if (log.isDebugEnabled())
347             log.debug(sm.getString("standardManager.loading", pathname));
348         FileInputStream JavaDoc fis = null;
349         ObjectInputStream JavaDoc ois = null;
350         Loader loader = null;
351         ClassLoader JavaDoc classLoader = null;
352         try {
353             fis = new FileInputStream JavaDoc(file.getAbsolutePath());
354             BufferedInputStream JavaDoc bis = new BufferedInputStream JavaDoc(fis);
355             if (container != null)
356                 loader = container.getLoader();
357             if (loader != null)
358                 classLoader = loader.getClassLoader();
359             if (classLoader != null) {
360                 if (log.isDebugEnabled())
361                     log.debug("Creating custom object input stream for class loader ");
362                 ois = new CustomObjectInputStream(bis, classLoader);
363             } else {
364                 if (log.isDebugEnabled())
365                     log.debug("Creating standard object input stream");
366                 ois = new ObjectInputStream JavaDoc(bis);
367             }
368         } catch (FileNotFoundException JavaDoc e) {
369             if (log.isDebugEnabled())
370                 log.debug("No persisted data file found");
371             return;
372         } catch (IOException JavaDoc e) {
373             log.error(sm.getString("standardManager.loading.ioe", e), e);
374             if (ois != null) {
375                 try {
376                     ois.close();
377                 } catch (IOException JavaDoc f) {
378                     ;
379                 }
380                 ois = null;
381             }
382             throw e;
383         }
384
385         // Load the previously unloaded active sessions
386
synchronized (sessions) {
387             try {
388                 Integer JavaDoc count = (Integer JavaDoc) ois.readObject();
389                 int n = count.intValue();
390                 if (log.isDebugEnabled())
391                     log.debug("Loading " + n + " persisted sessions");
392                 for (int i = 0; i < n; i++) {
393                     StandardSession session = getNewSession();
394                     session.readObjectData(ois);
395                     session.setManager(this);
396                     sessions.put(session.getIdInternal(), session);
397                     session.activate();
398                     session.endAccess();
399                 }
400             } catch (ClassNotFoundException JavaDoc e) {
401               log.error(sm.getString("standardManager.loading.cnfe", e), e);
402                 if (ois != null) {
403                     try {
404                         ois.close();
405                     } catch (IOException JavaDoc f) {
406                         ;
407                     }
408                     ois = null;
409                 }
410                 throw e;
411             } catch (IOException JavaDoc e) {
412               log.error(sm.getString("standardManager.loading.ioe", e), e);
413                 if (ois != null) {
414                     try {
415                         ois.close();
416                     } catch (IOException JavaDoc f) {
417                         ;
418                     }
419                     ois = null;
420                 }
421                 throw e;
422             } finally {
423                 // Close the input stream
424
try {
425                     if (ois != null)
426                         ois.close();
427                 } catch (IOException JavaDoc f) {
428                     // ignored
429
}
430
431                 // Delete the persistent storage file
432
if (file != null && file.exists() )
433                     file.delete();
434             }
435         }
436
437         if (log.isDebugEnabled())
438             log.debug("Finish: Loading persisted sessions");
439     }
440
441
442     /**
443      * Save any currently active sessions in the appropriate persistence
444      * mechanism, if any. If persistence is not supported, this method
445      * returns without doing anything.
446      *
447      * @exception IOException if an input/output error occurs
448      */

449     public void unload() throws IOException JavaDoc {
450         if (SecurityUtil.isPackageProtectionEnabled()){
451             try{
452                 AccessController.doPrivileged( new PrivilegedDoUnload() );
453             } catch (PrivilegedActionException JavaDoc ex){
454                 Exception JavaDoc exception = ex.getException();
455                 if (exception instanceof IOException JavaDoc){
456                     throw (IOException JavaDoc)exception;
457                 }
458                 if (log.isDebugEnabled())
459                     log.debug("Unreported exception in unLoad() "
460                         + exception);
461             }
462         } else {
463             doUnload();
464         }
465     }
466
467
468     /**
469      * Save any currently active sessions in the appropriate persistence
470      * mechanism, if any. If persistence is not supported, this method
471      * returns without doing anything.
472      *
473      * @exception IOException if an input/output error occurs
474      */

475     protected void doUnload() throws IOException JavaDoc {
476
477         if (log.isDebugEnabled())
478             log.debug("Unloading persisted sessions");
479
480         // Open an output stream to the specified pathname, if any
481
File JavaDoc file = file();
482         if (file == null)
483             return;
484         if (log.isDebugEnabled())
485             log.debug(sm.getString("standardManager.unloading", pathname));
486         FileOutputStream JavaDoc fos = null;
487         ObjectOutputStream JavaDoc oos = null;
488         try {
489             fos = new FileOutputStream JavaDoc(file.getAbsolutePath());
490             oos = new ObjectOutputStream JavaDoc(new BufferedOutputStream JavaDoc(fos));
491         } catch (IOException JavaDoc e) {
492             log.error(sm.getString("standardManager.unloading.ioe", e), e);
493             if (oos != null) {
494                 try {
495                     oos.close();
496                 } catch (IOException JavaDoc f) {
497                     ;
498                 }
499                 oos = null;
500             }
501             throw e;
502         }
503
504         // Write the number of active sessions, followed by the details
505
ArrayList JavaDoc list = new ArrayList JavaDoc();
506         synchronized (sessions) {
507             if (log.isDebugEnabled())
508                 log.debug("Unloading " + sessions.size() + " sessions");
509             try {
510                 oos.writeObject(new Integer JavaDoc(sessions.size()));
511                 Iterator JavaDoc elements = sessions.values().iterator();
512                 while (elements.hasNext()) {
513                     StandardSession session =
514                         (StandardSession) elements.next();
515                     list.add(session);
516                     ((StandardSession) session).passivate();
517                     session.writeObjectData(oos);
518                 }
519             } catch (IOException JavaDoc e) {
520                 log.error(sm.getString("standardManager.unloading.ioe", e), e);
521                 if (oos != null) {
522                     try {
523                         oos.close();
524                     } catch (IOException JavaDoc f) {
525                         ;
526                     }
527                     oos = null;
528                 }
529                 throw e;
530             }
531         }
532
533         // Flush and close the output stream
534
try {
535             oos.flush();
536             oos.close();
537             oos = null;
538         } catch (IOException JavaDoc e) {
539             if (oos != null) {
540                 try {
541                     oos.close();
542                 } catch (IOException JavaDoc f) {
543                     ;
544                 }
545                 oos = null;
546             }
547             throw e;
548         }
549
550         // Expire all the sessions we just wrote
551
if (log.isDebugEnabled())
552             log.debug("Expiring " + list.size() + " persisted sessions");
553         Iterator JavaDoc expires = list.iterator();
554         while (expires.hasNext()) {
555             StandardSession session = (StandardSession) expires.next();
556             try {
557                 session.expire(false);
558             } catch (Throwable JavaDoc t) {
559                 ;
560             } finally {
561                 session.recycle();
562             }
563         }
564
565         if (log.isDebugEnabled())
566             log.debug("Unloading complete");
567
568     }
569
570
571     // ------------------------------------------------------ Lifecycle Methods
572

573
574     /**
575      * Add a lifecycle event listener to this component.
576      *
577      * @param listener The listener to add
578      */

579     public void addLifecycleListener(LifecycleListener listener) {
580
581         lifecycle.addLifecycleListener(listener);
582
583     }
584
585
586     /**
587      * Get the lifecycle listeners associated with this lifecycle. If this
588      * Lifecycle has no listeners registered, a zero-length array is returned.
589      */

590     public LifecycleListener[] findLifecycleListeners() {
591
592         return lifecycle.findLifecycleListeners();
593
594     }
595
596
597     /**
598      * Remove a lifecycle event listener from this component.
599      *
600      * @param listener The listener to remove
601      */

602     public void removeLifecycleListener(LifecycleListener listener) {
603
604         lifecycle.removeLifecycleListener(listener);
605
606     }
607
608     /**
609      * Prepare for the beginning of active use of the public methods of this
610      * component. This method should be called after <code>configure()</code>,
611      * and before any of the public methods of the component are utilized.
612      *
613      * @exception LifecycleException if this component detects a fatal error
614      * that prevents this component from being used
615      */

616     public void start() throws LifecycleException {
617
618         if( ! initialized )
619             init();
620
621         // Validate and update our current component state
622
if (started) {
623             return;
624         }
625         lifecycle.fireLifecycleEvent(START_EVENT, null);
626         started = true;
627
628         // Force initialization of the random number generator
629
if (log.isDebugEnabled())
630             log.debug("Force random number initialization starting");
631         String JavaDoc dummy = generateSessionId();
632         if (log.isDebugEnabled())
633             log.debug("Force random number initialization completed");
634
635         // Load unloaded sessions, if any
636
try {
637             load();
638         } catch (Throwable JavaDoc t) {
639             log.error(sm.getString("standardManager.managerLoad"), t);
640         }
641
642     }
643
644
645     /**
646      * Gracefully terminate the active use of the public methods of this
647      * component. This method should be the last one called on a given
648      * instance of this component.
649      *
650      * @exception LifecycleException if this component detects a fatal error
651      * that needs to be reported
652      */

653     public void stop() throws LifecycleException {
654
655         if (log.isDebugEnabled())
656             log.debug("Stopping");
657
658         // Validate and update our current component state
659
if (!started)
660             throw new LifecycleException
661                 (sm.getString("standardManager.notStarted"));
662         lifecycle.fireLifecycleEvent(STOP_EVENT, null);
663         started = false;
664
665         // Write out sessions
666
try {
667             unload();
668         } catch (Throwable JavaDoc t) {
669             log.error(sm.getString("standardManager.managerUnload"), t);
670         }
671
672         // Expire all active sessions
673
Session sessions[] = findSessions();
674         for (int i = 0; i < sessions.length; i++) {
675             Session session = sessions[i];
676             try {
677                 if (session.isValid()) {
678                     session.expire();
679                 }
680             } catch (Throwable JavaDoc t) {
681                 ;
682             } finally {
683                 // Measure against memory leaking if references to the session
684
// object are kept in a shared field somewhere
685
session.recycle();
686             }
687         }
688
689         // Require a new random number generator if we are restarted
690
this.random = null;
691
692         if( initialized ) {
693             destroy();
694         }
695     }
696
697
698     // ----------------------------------------- PropertyChangeListener Methods
699

700
701     /**
702      * Process property change events from our associated Context.
703      *
704      * @param event The property change event that has occurred
705      */

706     public void propertyChange(PropertyChangeEvent JavaDoc event) {
707
708         // Validate the source of this event
709
if (!(event.getSource() instanceof Context JavaDoc))
710             return;
711         Context JavaDoc context = (Context JavaDoc) event.getSource();
712
713         // Process a relevant property change
714
if (event.getPropertyName().equals("sessionTimeout")) {
715             try {
716                 setMaxInactiveInterval
717                     ( ((Integer JavaDoc) event.getNewValue()).intValue()*60 );
718             } catch (NumberFormatException JavaDoc e) {
719                 log.error(sm.getString("standardManager.sessionTimeout",
720                                  event.getNewValue().toString()));
721             }
722         }
723
724     }
725
726
727     // ------------------------------------------------------ Protected Methods
728

729
730     /**
731      * Return a File object representing the pathname to our
732      * persistence file, if any.
733      */

734     protected File JavaDoc file() {
735
736         if ((pathname == null) || (pathname.length() == 0))
737             return (null);
738         File JavaDoc file = new File JavaDoc(pathname);
739         if (!file.isAbsolute()) {
740             if (container instanceof Context JavaDoc) {
741                 ServletContext JavaDoc servletContext =
742                     ((Context JavaDoc) container).getServletContext();
743                 File JavaDoc tempdir = (File JavaDoc)
744                     servletContext.getAttribute(Globals.WORK_DIR_ATTR);
745                 if (tempdir != null)
746                     file = new File JavaDoc(tempdir, pathname);
747             }
748         }
749 // if (!file.isAbsolute())
750
// return (null);
751
return (file);
752
753     }
754 }
755
Popular Tags