Home » apache-tomcat-6.0.26-src » org.apache » catalina » session » [javadoc | source]

    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.BufferedInputStream;
   24   import java.io.BufferedOutputStream;
   25   import java.io.File;
   26   import java.io.FileInputStream;
   27   import java.io.FileNotFoundException;
   28   import java.io.FileOutputStream;
   29   import java.io.IOException;
   30   import java.io.ObjectInputStream;
   31   import java.io.ObjectOutputStream;
   32   import java.security.AccessController;
   33   import java.security.PrivilegedActionException;
   34   import java.security.PrivilegedExceptionAction;
   35   import java.util.ArrayList;
   36   import java.util.Iterator;
   37   import javax.servlet.ServletContext;
   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: 612761 $ $Date: 2008-01-17 09:48:45 +0100 (Thu, 17 Jan 2008) $
   63    */
   64   
   65   public class StandardManager
   66       extends ManagerBase
   67       implements Lifecycle, PropertyChangeListener {
   68   
   69       // ---------------------------------------------------- Security Classes
   70       private class PrivilegedDoLoad
   71           implements PrivilegedExceptionAction {
   72   
   73           PrivilegedDoLoad() {
   74           }
   75   
   76           public Object run() throws Exception{
   77              doLoad();
   78              return null;
   79           }
   80       }
   81   
   82       private class PrivilegedDoUnload
   83           implements PrivilegedExceptionAction {
   84   
   85           PrivilegedDoUnload() {
   86           }
   87   
   88           public Object run() throws Exception{
   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 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 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 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))
  166               ((Context) 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)) {
  173               setMaxInactiveInterval
  174                   ( ((Context) this.container).getSessionTimeout()*60 );
  175               ((Context) 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 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(oldMaxActiveSessions),
  230                                      new Integer(this.maxActiveSessions));
  231   
  232       }
  233   
  234   
  235       /**
  236        * Return the descriptive short name of this Manager implementation.
  237        */
  238       public String getName() {
  239   
  240           return (name);
  241   
  242       }
  243   
  244   
  245       /**
  246        * Return the session persistence pathname, if any.
  247        */
  248       public String 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 pathname) {
  262   
  263           String 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 sessionId) {
  283   
  284           if ((maxActiveSessions >= 0) &&
  285               (sessions.size() >= maxActiveSessions)) {
  286               rejectedSessions++;
  287               throw new IllegalStateException
  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, IOException {
  306           if (SecurityUtil.isPackageProtectionEnabled()){
  307               try{
  308                   AccessController.doPrivileged( new PrivilegedDoLoad() );
  309               } catch (PrivilegedActionException ex){
  310                   Exception exception = ex.getException();
  311                   if (exception instanceof ClassNotFoundException){
  312                       throw (ClassNotFoundException)exception;
  313                   } else if (exception instanceof IOException){
  314                       throw (IOException)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, IOException {
  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 file = file();
  344           if (file == null)
  345               return;
  346           if (log.isDebugEnabled())
  347               log.debug(sm.getString("standardManager.loading", pathname));
  348           FileInputStream fis = null;
  349           ObjectInputStream ois = null;
  350           Loader loader = null;
  351           ClassLoader classLoader = null;
  352           try {
  353               fis = new FileInputStream(file.getAbsolutePath());
  354               BufferedInputStream bis = new BufferedInputStream(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(bis);
  367               }
  368           } catch (FileNotFoundException e) {
  369               if (log.isDebugEnabled())
  370                   log.debug("No persisted data file found");
  371               return;
  372           } catch (IOException e) {
  373               log.error(sm.getString("standardManager.loading.ioe", e), e);
  374               if (ois != null) {
  375                   try {
  376                       ois.close();
  377                   } catch (IOException 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 count = (Integer) 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                       sessionCounter++;
  399                   }
  400               } catch (ClassNotFoundException e) {
  401                   log.error(sm.getString("standardManager.loading.cnfe", e), e);
  402                   if (ois != null) {
  403                       try {
  404                           ois.close();
  405                       } catch (IOException f) {
  406                           ;
  407                       }
  408                       ois = null;
  409                   }
  410                   throw e;
  411               } catch (IOException e) {
  412                   log.error(sm.getString("standardManager.loading.ioe", e), e);
  413                   if (ois != null) {
  414                       try {
  415                           ois.close();
  416                       } catch (IOException 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 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 {
  450           if (SecurityUtil.isPackageProtectionEnabled()){
  451               try{
  452                   AccessController.doPrivileged( new PrivilegedDoUnload() );
  453               } catch (PrivilegedActionException ex){
  454                   Exception exception = ex.getException();
  455                   if (exception instanceof IOException){
  456                       throw (IOException)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 {
  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 file = file();
  482           if (file == null)
  483               return;
  484           if (log.isDebugEnabled())
  485               log.debug(sm.getString("standardManager.unloading", pathname));
  486           FileOutputStream fos = null;
  487           ObjectOutputStream oos = null;
  488           try {
  489               fos = new FileOutputStream(file.getAbsolutePath());
  490               oos = new ObjectOutputStream(new BufferedOutputStream(fos));
  491           } catch (IOException e) {
  492               log.error(sm.getString("standardManager.unloading.ioe", e), e);
  493               if (oos != null) {
  494                   try {
  495                       oos.close();
  496                   } catch (IOException 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 list = new ArrayList();
  506           synchronized (sessions) {
  507               if (log.isDebugEnabled())
  508                   log.debug("Unloading " + sessions.size() + " sessions");
  509               try {
  510                   oos.writeObject(new Integer(sessions.size()));
  511                   Iterator 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 e) {
  520                   log.error(sm.getString("standardManager.unloading.ioe", e), e);
  521                   if (oos != null) {
  522                       try {
  523                           oos.close();
  524                       } catch (IOException 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 e) {
  539               if (oos != null) {
  540                   try {
  541                       oos.close();
  542                   } catch (IOException 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 expires = list.iterator();
  554           while (expires.hasNext()) {
  555               StandardSession session = (StandardSession) expires.next();
  556               try {
  557                   session.expire(false);
  558               } catch (Throwable 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 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 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 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 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 event) {
  707   
  708           // Validate the source of this event
  709           if (!(event.getSource() instanceof Context))
  710               return;
  711           Context context = (Context) event.getSource();
  712   
  713           // Process a relevant property change
  714           if (event.getPropertyName().equals("sessionTimeout")) {
  715               try {
  716                   setMaxInactiveInterval
  717                       ( ((Integer) event.getNewValue()).intValue()*60 );
  718               } catch (NumberFormatException 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 file() {
  735   
  736           if ((pathname == null) || (pathname.length() == 0))
  737               return (null);
  738           File file = new File(pathname);
  739           if (!file.isAbsolute()) {
  740               if (container instanceof Context) {
  741                   ServletContext servletContext =
  742                       ((Context) container).getServletContext();
  743                   File tempdir = (File)
  744                       servletContext.getAttribute(Globals.WORK_DIR_ATTR);
  745                   if (tempdir != null)
  746                       file = new File(tempdir, pathname);
  747               }
  748           }
  749   //        if (!file.isAbsolute())
  750   //            return (null);
  751           return (file);
  752   
  753       }
  754   }

Home » apache-tomcat-6.0.26-src » org.apache » catalina » session » [javadoc | source]