Save This Page
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   
   22   import java.beans.PropertyChangeListener;
   23   import java.beans.PropertyChangeSupport;
   24   import java.io.DataInputStream;
   25   import java.io.File;
   26   import java.io.FileInputStream;
   27   import java.io.IOException;
   28   import java.lang.reflect.Method;
   29   import java.security.AccessController;
   30   import java.security.MessageDigest;
   31   import java.security.NoSuchAlgorithmException;
   32   import java.security.PrivilegedAction;
   33   import java.util.Date;
   34   import java.util.Enumeration;
   35   import java.util.HashMap;
   36   import java.util.Iterator;
   37   import java.util.Map;
   38   import java.util.Random;
   39   import java.util.concurrent.ConcurrentHashMap;
   40   
   41   import javax.management.MBeanRegistration;
   42   import javax.management.MBeanServer;
   43   import javax.management.ObjectName;
   44   
   45   import org.apache.catalina.Container;
   46   import org.apache.catalina.Engine;
   47   import org.apache.catalina.Globals;
   48   import org.apache.catalina.Manager;
   49   import org.apache.catalina.Session;
   50   import org.apache.catalina.core.StandardContext;
   51   import org.apache.catalina.core.StandardHost;
   52   import org.apache.catalina.util.StringManager;
   53   import org.apache.juli.logging.Log;
   54   import org.apache.juli.logging.LogFactory;
   55   import org.apache.tomcat.util.modeler.Registry;
   56   
   57   
   58   /**
   59    * Minimal implementation of the <b>Manager</b> interface that supports
   60    * no session persistence or distributable capabilities.  This class may
   61    * be subclassed to create more sophisticated Manager implementations.
   62    *
   63    * @author Craig R. McClanahan
   64    * @version $Revision: 892545 $ $Date: 2009-12-20 02:04:17 +0100 (Sun, 20 Dec 2009) $
   65    */
   66   
   67   public abstract class ManagerBase implements Manager, MBeanRegistration {
   68       protected Log log = LogFactory.getLog(ManagerBase.class);
   69   
   70       // ----------------------------------------------------- Instance Variables
   71   
   72       protected DataInputStream randomIS=null;
   73       protected String devRandomSource="/dev/urandom";
   74   
   75       /**
   76        * The default message digest algorithm to use if we cannot use
   77        * the requested one.
   78        */
   79       protected static final String DEFAULT_ALGORITHM = "MD5";
   80   
   81   
   82       /**
   83        * The message digest algorithm to be used when generating session
   84        * identifiers.  This must be an algorithm supported by the
   85        * <code>java.security.MessageDigest</code> class on your platform.
   86        */
   87       protected String algorithm = DEFAULT_ALGORITHM;
   88   
   89   
   90       /**
   91        * The Container with which this Manager is associated.
   92        */
   93       protected Container container;
   94   
   95   
   96       /**
   97        * Return the MessageDigest implementation to be used when
   98        * creating session identifiers.
   99        */
  100       protected MessageDigest digest = null;
  101   
  102   
  103       /**
  104        * The distributable flag for Sessions created by this Manager.  If this
  105        * flag is set to <code>true</code>, any user attributes added to a
  106        * session controlled by this Manager must be Serializable.
  107        */
  108       protected boolean distributable;
  109   
  110   
  111       /**
  112        * A String initialization parameter used to increase the entropy of
  113        * the initialization of our random number generator.
  114        */
  115       protected String entropy = null;
  116   
  117   
  118       /**
  119        * The descriptive information string for this implementation.
  120        */
  121       private static final String info = "ManagerBase/1.0";
  122   
  123   
  124       /**
  125        * The default maximum inactive interval for Sessions created by
  126        * this Manager.
  127        */
  128       protected int maxInactiveInterval = 60;
  129   
  130   
  131       /**
  132        * The session id length of Sessions created by this Manager.
  133        */
  134       protected int sessionIdLength = 16;
  135   
  136   
  137       /**
  138        * The descriptive name of this Manager implementation (for logging).
  139        */
  140       protected static String name = "ManagerBase";
  141   
  142   
  143       /**
  144        * A random number generator to use when generating session identifiers.
  145        */
  146       protected Random random = null;
  147   
  148   
  149       /**
  150        * The Java class name of the random number generator class to be used
  151        * when generating session identifiers.
  152        */
  153       protected String randomClass = "java.security.SecureRandom";
  154   
  155   
  156       /**
  157        * The longest time (in seconds) that an expired session had been alive.
  158        */
  159       protected int sessionMaxAliveTime;
  160   
  161   
  162       /**
  163        * Average time (in seconds) that expired sessions had been alive.
  164        */
  165       protected int sessionAverageAliveTime;
  166   
  167   
  168       /**
  169        * Number of sessions that have expired.
  170        */
  171       protected int expiredSessions = 0;
  172   
  173   
  174       /**
  175        * The set of currently active Sessions for this Manager, keyed by
  176        * session identifier.
  177        */
  178       protected Map<String, Session> sessions = new ConcurrentHashMap<String, Session>();
  179   
  180       // Number of sessions created by this manager
  181       protected int sessionCounter=0;
  182   
  183       protected int maxActive=0;
  184   
  185       // number of duplicated session ids - anything >0 means we have problems
  186       protected int duplicates=0;
  187   
  188       protected boolean initialized=false;
  189       
  190       /**
  191        * Processing time during session expiration.
  192        */
  193       protected long processingTime = 0;
  194   
  195       /**
  196        * Iteration count for background processing.
  197        */
  198       private int count = 0;
  199   
  200   
  201       /**
  202        * Frequency of the session expiration, and related manager operations.
  203        * Manager operations will be done once for the specified amount of
  204        * backgrondProcess calls (ie, the lower the amount, the most often the
  205        * checks will occur).
  206        */
  207       protected int processExpiresFrequency = 6;
  208   
  209       /**
  210        * The string manager for this package.
  211        */
  212       protected static StringManager sm =
  213           StringManager.getManager(Constants.Package);
  214   
  215       /**
  216        * The property change support for this component.
  217        */
  218       protected PropertyChangeSupport support = new PropertyChangeSupport(this);
  219       
  220       // ------------------------------------------------------------- Security classes
  221   
  222   
  223       private class PrivilegedSetRandomFile
  224               implements PrivilegedAction<DataInputStream>{
  225           
  226           public PrivilegedSetRandomFile(String s) {
  227               devRandomSource = s;
  228           }
  229           
  230           public DataInputStream run(){
  231               try {
  232                   File f=new File( devRandomSource );
  233                   if( ! f.exists() ) return null;
  234                   randomIS= new DataInputStream( new FileInputStream(f));
  235                   randomIS.readLong();
  236                   if( log.isDebugEnabled() )
  237                       log.debug( "Opening " + devRandomSource );
  238                   return randomIS;
  239               } catch (IOException ex){
  240                   log.warn("Error reading " + devRandomSource, ex);
  241                   if (randomIS != null) {
  242                       try {
  243                           randomIS.close();
  244                       } catch (Exception e) {
  245                           log.warn("Failed to close randomIS.");
  246                       }
  247                   }
  248                   devRandomSource = null;
  249                   randomIS=null;
  250                   return null;
  251               }
  252           }
  253       }
  254   
  255   
  256       // ------------------------------------------------------------- Properties
  257   
  258       /**
  259        * Return the message digest algorithm for this Manager.
  260        */
  261       public String getAlgorithm() {
  262   
  263           return (this.algorithm);
  264   
  265       }
  266   
  267   
  268       /**
  269        * Set the message digest algorithm for this Manager.
  270        *
  271        * @param algorithm The new message digest algorithm
  272        */
  273       public void setAlgorithm(String algorithm) {
  274   
  275           String oldAlgorithm = this.algorithm;
  276           this.algorithm = algorithm;
  277           support.firePropertyChange("algorithm", oldAlgorithm, this.algorithm);
  278   
  279       }
  280   
  281   
  282       /**
  283        * Return the Container with which this Manager is associated.
  284        */
  285       public Container getContainer() {
  286   
  287           return (this.container);
  288   
  289       }
  290   
  291   
  292       /**
  293        * Set the Container with which this Manager is associated.
  294        *
  295        * @param container The newly associated Container
  296        */
  297       public void setContainer(Container container) {
  298   
  299           Container oldContainer = this.container;
  300           this.container = container;
  301           support.firePropertyChange("container", oldContainer, this.container);
  302       }
  303   
  304   
  305       /** Returns the name of the implementation class.
  306        */
  307       public String getClassName() {
  308           return this.getClass().getName();
  309       }
  310   
  311   
  312       /**
  313        * Return the MessageDigest object to be used for calculating
  314        * session identifiers.  If none has been created yet, initialize
  315        * one the first time this method is called.
  316        */
  317       public synchronized MessageDigest getDigest() {
  318   
  319           if (this.digest == null) {
  320               long t1=System.currentTimeMillis();
  321               if (log.isDebugEnabled())
  322                   log.debug(sm.getString("managerBase.getting", algorithm));
  323               try {
  324                   this.digest = MessageDigest.getInstance(algorithm);
  325               } catch (NoSuchAlgorithmException e) {
  326                   log.error(sm.getString("managerBase.digest", algorithm), e);
  327                   try {
  328                       this.digest = MessageDigest.getInstance(DEFAULT_ALGORITHM);
  329                   } catch (NoSuchAlgorithmException f) {
  330                       log.error(sm.getString("managerBase.digest",
  331                                        DEFAULT_ALGORITHM), e);
  332                       this.digest = null;
  333                   }
  334               }
  335               if (log.isDebugEnabled())
  336                   log.debug(sm.getString("managerBase.gotten"));
  337               long t2=System.currentTimeMillis();
  338               if( log.isDebugEnabled() )
  339                   log.debug("getDigest() " + (t2-t1));
  340           }
  341   
  342           return (this.digest);
  343   
  344       }
  345   
  346   
  347       /**
  348        * Return the distributable flag for the sessions supported by
  349        * this Manager.
  350        */
  351       public boolean getDistributable() {
  352   
  353           return (this.distributable);
  354   
  355       }
  356   
  357   
  358       /**
  359        * Set the distributable flag for the sessions supported by this
  360        * Manager.  If this flag is set, all user data objects added to
  361        * sessions associated with this manager must implement Serializable.
  362        *
  363        * @param distributable The new distributable flag
  364        */
  365       public void setDistributable(boolean distributable) {
  366   
  367           boolean oldDistributable = this.distributable;
  368           this.distributable = distributable;
  369           support.firePropertyChange("distributable",
  370                                      new Boolean(oldDistributable),
  371                                      new Boolean(this.distributable));
  372   
  373       }
  374   
  375   
  376       /**
  377        * Return the entropy increaser value, or compute a semi-useful value
  378        * if this String has not yet been set.
  379        */
  380       public String getEntropy() {
  381   
  382           // Calculate a semi-useful value if this has not been set
  383           if (this.entropy == null) {
  384               // Use APR to get a crypto secure entropy value
  385               byte[] result = new byte[32];
  386               boolean apr = false;
  387               try {
  388                   String methodName = "random";
  389                   Class paramTypes[] = new Class[2];
  390                   paramTypes[0] = result.getClass();
  391                   paramTypes[1] = int.class;
  392                   Object paramValues[] = new Object[2];
  393                   paramValues[0] = result;
  394                   paramValues[1] = new Integer(32);
  395                   Method method = Class.forName("org.apache.tomcat.jni.OS")
  396                       .getMethod(methodName, paramTypes);
  397                   method.invoke(null, paramValues);
  398                   apr = true;
  399               } catch (Throwable t) {
  400                   // Ignore
  401               }
  402               if (apr) {
  403                   setEntropy(new String(result));
  404               } else {
  405                   setEntropy(this.toString());
  406               }
  407           }
  408   
  409           return (this.entropy);
  410   
  411       }
  412   
  413   
  414       /**
  415        * Set the entropy increaser value.
  416        *
  417        * @param entropy The new entropy increaser value
  418        */
  419       public void setEntropy(String entropy) {
  420   
  421           String oldEntropy = entropy;
  422           this.entropy = entropy;
  423           support.firePropertyChange("entropy", oldEntropy, this.entropy);
  424   
  425       }
  426   
  427   
  428       /**
  429        * Return descriptive information about this Manager implementation and
  430        * the corresponding version number, in the format
  431        * <code>&lt;description&gt;/&lt;version&gt;</code>.
  432        */
  433       public String getInfo() {
  434   
  435           return (info);
  436   
  437       }
  438   
  439   
  440       /**
  441        * Return the default maximum inactive interval (in seconds)
  442        * for Sessions created by this Manager.
  443        */
  444       public int getMaxInactiveInterval() {
  445   
  446           return (this.maxInactiveInterval);
  447   
  448       }
  449   
  450   
  451       /**
  452        * Set the default maximum inactive interval (in seconds)
  453        * for Sessions created by this Manager.
  454        *
  455        * @param interval The new default value
  456        */
  457       public void setMaxInactiveInterval(int interval) {
  458   
  459           int oldMaxInactiveInterval = this.maxInactiveInterval;
  460           this.maxInactiveInterval = interval;
  461           support.firePropertyChange("maxInactiveInterval",
  462                                      new Integer(oldMaxInactiveInterval),
  463                                      new Integer(this.maxInactiveInterval));
  464   
  465       }
  466   
  467   
  468       /**
  469        * Gets the session id length (in bytes) of Sessions created by
  470        * this Manager.
  471        *
  472        * @return The session id length
  473        */
  474       public int getSessionIdLength() {
  475   
  476           return (this.sessionIdLength);
  477   
  478       }
  479   
  480   
  481       /**
  482        * Sets the session id length (in bytes) for Sessions created by this
  483        * Manager.
  484        *
  485        * @param idLength The session id length
  486        */
  487       public void setSessionIdLength(int idLength) {
  488   
  489           int oldSessionIdLength = this.sessionIdLength;
  490           this.sessionIdLength = idLength;
  491           support.firePropertyChange("sessionIdLength",
  492                                      new Integer(oldSessionIdLength),
  493                                      new Integer(this.sessionIdLength));
  494   
  495       }
  496   
  497   
  498       /**
  499        * Return the descriptive short name of this Manager implementation.
  500        */
  501       public String getName() {
  502   
  503           return (name);
  504   
  505       }
  506   
  507       /** 
  508        * Use /dev/random-type special device. This is new code, but may reduce
  509        * the big delay in generating the random.
  510        *
  511        *  You must specify a path to a random generator file. Use /dev/urandom
  512        *  for linux ( or similar ) systems. Use /dev/random for maximum security
  513        *  ( it may block if not enough "random" exist ). You can also use
  514        *  a pipe that generates random.
  515        *
  516        *  The code will check if the file exists, and default to java Random
  517        *  if not found. There is a significant performance difference, very
  518        *  visible on the first call to getSession ( like in the first JSP )
  519        *  - so use it if available.
  520        */
  521       public void setRandomFile( String s ) {
  522           // as a hack, you can use a static file - and generate the same
  523           // session ids ( good for strange debugging )
  524           if (Globals.IS_SECURITY_ENABLED){
  525               randomIS = AccessController.doPrivileged(new PrivilegedSetRandomFile(s));
  526           } else {
  527               try{
  528                   devRandomSource=s;
  529                   File f=new File( devRandomSource );
  530                   if( ! f.exists() ) return;
  531                   randomIS= new DataInputStream( new FileInputStream(f));
  532                   randomIS.readLong();
  533                   if( log.isDebugEnabled() )
  534                       log.debug( "Opening " + devRandomSource );
  535               } catch( IOException ex ) {
  536                   log.warn("Error reading " + devRandomSource, ex);
  537                   if (randomIS != null) {
  538                       try {
  539                           randomIS.close();
  540                       } catch (Exception e) {
  541                           log.warn("Failed to close randomIS.");
  542                       }
  543                   }
  544                   devRandomSource = null;
  545                   randomIS=null;
  546               }
  547           }
  548       }
  549   
  550       public String getRandomFile() {
  551           return devRandomSource;
  552       }
  553   
  554   
  555       /**
  556        * Return the random number generator instance we should use for
  557        * generating session identifiers.  If there is no such generator
  558        * currently defined, construct and seed a new one.
  559        */
  560       public Random getRandom() {
  561           if (this.random == null) {
  562               // Calculate the new random number generator seed
  563               long seed = System.currentTimeMillis();
  564               long t1 = seed;
  565               char entropy[] = getEntropy().toCharArray();
  566               for (int i = 0; i < entropy.length; i++) {
  567                   long update = ((byte) entropy[i]) << ((i % 8) * 8);
  568                   seed ^= update;
  569               }
  570               try {
  571                   // Construct and seed a new random number generator
  572                   Class clazz = Class.forName(randomClass);
  573                   this.random = (Random) clazz.newInstance();
  574                   this.random.setSeed(seed);
  575               } catch (Exception e) {
  576                   // Fall back to the simple case
  577                   log.error(sm.getString("managerBase.random", randomClass),
  578                           e);
  579                   this.random = new java.util.Random();
  580                   this.random.setSeed(seed);
  581               }
  582               if(log.isDebugEnabled()) {
  583                   long t2=System.currentTimeMillis();
  584                   if( (t2-t1) > 100 )
  585                       log.debug(sm.getString("managerBase.seeding", randomClass) + " " + (t2-t1));
  586               }
  587           }
  588           
  589           return (this.random);
  590   
  591       }
  592   
  593   
  594       /**
  595        * Return the random number generator class name.
  596        */
  597       public String getRandomClass() {
  598   
  599           return (this.randomClass);
  600   
  601       }
  602   
  603   
  604       /**
  605        * Set the random number generator class name.
  606        *
  607        * @param randomClass The new random number generator class name
  608        */
  609       public void setRandomClass(String randomClass) {
  610   
  611           String oldRandomClass = this.randomClass;
  612           this.randomClass = randomClass;
  613           support.firePropertyChange("randomClass", oldRandomClass,
  614                                      this.randomClass);
  615   
  616       }
  617   
  618   
  619       /**
  620        * Gets the number of sessions that have expired.
  621        *
  622        * @return Number of sessions that have expired
  623        */
  624       public int getExpiredSessions() {
  625           return expiredSessions;
  626       }
  627   
  628   
  629       /**
  630        * Sets the number of sessions that have expired.
  631        *
  632        * @param expiredSessions Number of sessions that have expired
  633        */
  634       public void setExpiredSessions(int expiredSessions) {
  635           this.expiredSessions = expiredSessions;
  636       }
  637   
  638       public long getProcessingTime() {
  639           return processingTime;
  640       }
  641   
  642   
  643       public void setProcessingTime(long processingTime) {
  644           this.processingTime = processingTime;
  645       }
  646       
  647       /**
  648        * Return the frequency of manager checks.
  649        */
  650       public int getProcessExpiresFrequency() {
  651   
  652           return (this.processExpiresFrequency);
  653   
  654       }
  655   
  656       /**
  657        * Set the manager checks frequency.
  658        *
  659        * @param processExpiresFrequency the new manager checks frequency
  660        */
  661       public void setProcessExpiresFrequency(int processExpiresFrequency) {
  662   
  663           if (processExpiresFrequency <= 0) {
  664               return;
  665           }
  666   
  667           int oldProcessExpiresFrequency = this.processExpiresFrequency;
  668           this.processExpiresFrequency = processExpiresFrequency;
  669           support.firePropertyChange("processExpiresFrequency",
  670                                      new Integer(oldProcessExpiresFrequency),
  671                                      new Integer(this.processExpiresFrequency));
  672   
  673       }
  674       // --------------------------------------------------------- Public Methods
  675   
  676   
  677       /**
  678        * Implements the Manager interface, direct call to processExpires
  679        */
  680       public void backgroundProcess() {
  681           count = (count + 1) % processExpiresFrequency;
  682           if (count == 0)
  683               processExpires();
  684       }
  685   
  686       /**
  687        * Invalidate all sessions that have expired.
  688        */
  689       public void processExpires() {
  690   
  691           long timeNow = System.currentTimeMillis();
  692           Session sessions[] = findSessions();
  693           int expireHere = 0 ;
  694           
  695           if(log.isDebugEnabled())
  696               log.debug("Start expire sessions " + getName() + " at " + timeNow + " sessioncount " + sessions.length);
  697           for (int i = 0; i < sessions.length; i++) {
  698               if (sessions[i]!=null && !sessions[i].isValid()) {
  699                   expireHere++;
  700               }
  701           }
  702           long timeEnd = System.currentTimeMillis();
  703           if(log.isDebugEnabled())
  704                log.debug("End expire sessions " + getName() + " processingTime " + (timeEnd - timeNow) + " expired sessions: " + expireHere);
  705           processingTime += ( timeEnd - timeNow );
  706   
  707       }
  708   
  709       public void destroy() {
  710           if( oname != null )
  711               Registry.getRegistry(null, null).unregisterComponent(oname);
  712           if (randomIS!=null) {
  713               try {
  714                   randomIS.close();
  715               } catch (IOException ioe) {
  716                   log.warn("Failed to close randomIS.");
  717               }
  718               randomIS=null;
  719           }
  720   
  721           initialized=false;
  722           oname = null;
  723       }
  724       
  725       public void init() {
  726           if( initialized ) return;
  727           initialized=true;        
  728           
  729           log = LogFactory.getLog(ManagerBase.class);
  730           
  731           if( oname==null ) {
  732               try {
  733                   StandardContext ctx=(StandardContext)this.getContainer();
  734                   Engine eng=(Engine)ctx.getParent().getParent();
  735                   domain=ctx.getEngineName();
  736                   distributable = ctx.getDistributable();
  737                   StandardHost hst=(StandardHost)ctx.getParent();
  738                   String path = ctx.getPath();
  739                   if (path.equals("")) {
  740                       path = "/";
  741                   }   
  742                   oname=new ObjectName(domain + ":type=Manager,path="
  743                   + path + ",host=" + hst.getName());
  744                   Registry.getRegistry(null, null).registerComponent(this, oname, null );
  745               } catch (Exception e) {
  746                   log.error("Error registering ",e);
  747               }
  748           }
  749           
  750           // Initialize random number generation
  751           getRandomBytes(new byte[16]);
  752           
  753           if(log.isDebugEnabled())
  754               log.debug("Registering " + oname );
  755                  
  756       }
  757   
  758       /**
  759        * Add this Session to the set of active Sessions for this Manager.
  760        *
  761        * @param session Session to be added
  762        */
  763       public void add(Session session) {
  764   
  765           sessions.put(session.getIdInternal(), session);
  766           int size = sessions.size();
  767           if( size > maxActive ) {
  768               maxActive = size;
  769           }
  770       }
  771   
  772   
  773       /**
  774        * Add a property change listener to this component.
  775        *
  776        * @param listener The listener to add
  777        */
  778       public void addPropertyChangeListener(PropertyChangeListener listener) {
  779   
  780           support.addPropertyChangeListener(listener);
  781   
  782       }
  783   
  784   
  785       /**
  786        * Construct and return a new session object, based on the default
  787        * settings specified by this Manager's properties.  The session
  788        * id will be assigned by this method, and available via the getId()
  789        * method of the returned session.  If a new session cannot be created
  790        * for any reason, return <code>null</code>.
  791        * 
  792        * @exception IllegalStateException if a new session cannot be
  793        *  instantiated for any reason
  794        * @deprecated
  795        */
  796       public Session createSession() {
  797           return createSession(null);
  798       }
  799       
  800       
  801       /**
  802        * Construct and return a new session object, based on the default
  803        * settings specified by this Manager's properties.  The session
  804        * id specified will be used as the session id.  
  805        * If a new session cannot be created for any reason, return 
  806        * <code>null</code>.
  807        * 
  808        * @param sessionId The session id which should be used to create the
  809        *  new session; if <code>null</code>, a new session id will be
  810        *  generated
  811        * @exception IllegalStateException if a new session cannot be
  812        *  instantiated for any reason
  813        */
  814       public Session createSession(String sessionId) {
  815           
  816           // Recycle or create a Session instance
  817           Session session = createEmptySession();
  818   
  819           // Initialize the properties of the new session and return it
  820           session.setNew(true);
  821           session.setValid(true);
  822           session.setCreationTime(System.currentTimeMillis());
  823           session.setMaxInactiveInterval(this.maxInactiveInterval);
  824           if (sessionId == null) {
  825               sessionId = generateSessionId();
  826           // FIXME WHy we need no duplication check?
  827           /*         
  828                synchronized (sessions) {
  829                   while (sessions.get(sessionId) != null) { // Guarantee
  830                       // uniqueness
  831                       duplicates++;
  832                       sessionId = generateSessionId();
  833                   }
  834               }
  835           */
  836               
  837               // FIXME: Code to be used in case route replacement is needed
  838               /*
  839           } else {
  840               String jvmRoute = getJvmRoute();
  841               if (getJvmRoute() != null) {
  842                   String requestJvmRoute = null;
  843                   int index = sessionId.indexOf(".");
  844                   if (index > 0) {
  845                       requestJvmRoute = sessionId
  846                               .substring(index + 1, sessionId.length());
  847                   }
  848                   if (requestJvmRoute != null && !requestJvmRoute.equals(jvmRoute)) {
  849                       sessionId = sessionId.substring(0, index) + "." + jvmRoute;
  850                   }
  851               }
  852               */
  853           }
  854           session.setId(sessionId);
  855           sessionCounter++;
  856   
  857           return (session);
  858   
  859       }
  860       
  861       
  862       /**
  863        * Get a session from the recycled ones or create a new empty one.
  864        * The PersistentManager manager does not need to create session data
  865        * because it reads it from the Store.
  866        */
  867       public Session createEmptySession() {
  868           return (getNewSession());
  869       }
  870   
  871   
  872       /**
  873        * Return the active Session, associated with this Manager, with the
  874        * specified session id (if any); otherwise return <code>null</code>.
  875        *
  876        * @param id The session id for the session to be returned
  877        *
  878        * @exception IllegalStateException if a new session cannot be
  879        *  instantiated for any reason
  880        * @exception IOException if an input/output error occurs while
  881        *  processing this request
  882        */
  883       public Session findSession(String id) throws IOException {
  884   
  885           if (id == null)
  886               return (null);
  887           return (Session) sessions.get(id);
  888   
  889       }
  890   
  891   
  892       /**
  893        * Return the set of active Sessions associated with this Manager.
  894        * If this Manager has no active Sessions, a zero-length array is returned.
  895        */
  896       public Session[] findSessions() {
  897   
  898           return sessions.values().toArray(new Session[0]);
  899   
  900       }
  901   
  902   
  903       /**
  904        * Remove this Session from the active Sessions for this Manager.
  905        *
  906        * @param session Session to be removed
  907        */
  908       public void remove(Session session) {
  909   
  910           sessions.remove(session.getIdInternal());
  911   
  912       }
  913   
  914   
  915       /**
  916        * Remove a property change listener from this component.
  917        *
  918        * @param listener The listener to remove
  919        */
  920       public void removePropertyChangeListener(PropertyChangeListener listener) {
  921   
  922           support.removePropertyChangeListener(listener);
  923   
  924       }
  925   
  926   
  927       /**
  928        * Change the session ID of the current session to a new randomly generated
  929        * session ID.
  930        * 
  931        * @param session   The session to change the session ID for
  932        */
  933       public void changeSessionId(Session session) {
  934           session.setId(generateSessionId());
  935       }
  936       
  937       
  938       // ------------------------------------------------------ Protected Methods
  939   
  940   
  941       /**
  942        * Get new session class to be used in the doLoad() method.
  943        */
  944       protected StandardSession getNewSession() {
  945           return new StandardSession(this);
  946       }
  947   
  948   
  949       protected void getRandomBytes(byte bytes[]) {
  950           // Generate a byte array containing a session identifier
  951           if (devRandomSource != null && randomIS == null) {
  952               setRandomFile(devRandomSource);
  953           }
  954           if (randomIS != null) {
  955               try {
  956                   int len = randomIS.read(bytes);
  957                   if (len == bytes.length) {
  958                       return;
  959                   }
  960                   if(log.isDebugEnabled())
  961                       log.debug("Got " + len + " " + bytes.length );
  962               } catch (Exception ex) {
  963                   // Ignore
  964               }
  965               devRandomSource = null;
  966               
  967               try {
  968                   randomIS.close();
  969               } catch (Exception e) {
  970                   log.warn("Failed to close randomIS.");
  971               }
  972               
  973               randomIS = null;
  974           }
  975           getRandom().nextBytes(bytes);
  976       }
  977   
  978   
  979       /**
  980        * Generate and return a new session identifier.
  981        */
  982       protected synchronized String generateSessionId() {
  983   
  984           byte random[] = new byte[16];
  985           String jvmRoute = getJvmRoute();
  986           String result = null;
  987   
  988           // Render the result as a String of hexadecimal digits
  989           StringBuffer buffer = new StringBuffer();
  990           do {
  991               int resultLenBytes = 0;
  992               if (result != null) {
  993                   buffer = new StringBuffer();
  994                   duplicates++;
  995               }
  996   
  997               while (resultLenBytes < this.sessionIdLength) {
  998                   getRandomBytes(random);
  999                   random = getDigest().digest(random);
 1000                   for (int j = 0;
 1001                   j < random.length && resultLenBytes < this.sessionIdLength;
 1002                   j++) {
 1003                       byte b1 = (byte) ((random[j] & 0xf0) >> 4);
 1004                       byte b2 = (byte) (random[j] & 0x0f);
 1005                       if (b1 < 10)
 1006                           buffer.append((char) ('0' + b1));
 1007                       else
 1008                           buffer.append((char) ('A' + (b1 - 10)));
 1009                       if (b2 < 10)
 1010                           buffer.append((char) ('0' + b2));
 1011                       else
 1012                           buffer.append((char) ('A' + (b2 - 10)));
 1013                       resultLenBytes++;
 1014                   }
 1015               }
 1016               if (jvmRoute != null) {
 1017                   buffer.append('.').append(jvmRoute);
 1018               }
 1019               result = buffer.toString();
 1020           } while (sessions.containsKey(result));
 1021           return (result);
 1022   
 1023       }
 1024   
 1025   
 1026       // ------------------------------------------------------ Protected Methods
 1027   
 1028   
 1029       /**
 1030        * Retrieve the enclosing Engine for this Manager.
 1031        *
 1032        * @return an Engine object (or null).
 1033        */
 1034       public Engine getEngine() {
 1035           Engine e = null;
 1036           for (Container c = getContainer(); e == null && c != null ; c = c.getParent()) {
 1037               if (c != null && c instanceof Engine) {
 1038                   e = (Engine)c;
 1039               }
 1040           }
 1041           return e;
 1042       }
 1043   
 1044   
 1045       /**
 1046        * Retrieve the JvmRoute for the enclosing Engine.
 1047        * @return the JvmRoute or null.
 1048        */
 1049       public String getJvmRoute() {
 1050           Engine e = getEngine();
 1051           return e == null ? null : e.getJvmRoute();
 1052       }
 1053   
 1054   
 1055       // -------------------------------------------------------- Package Methods
 1056   
 1057   
 1058       public void setSessionCounter(int sessionCounter) {
 1059           this.sessionCounter = sessionCounter;
 1060       }
 1061   
 1062   
 1063       /** 
 1064        * Total sessions created by this manager.
 1065        *
 1066        * @return sessions created
 1067        */
 1068       public int getSessionCounter() {
 1069           return sessionCounter;
 1070       }
 1071   
 1072   
 1073       /** 
 1074        * Number of duplicated session IDs generated by the random source.
 1075        * Anything bigger than 0 means problems.
 1076        *
 1077        * @return The count of duplicates
 1078        */
 1079       public int getDuplicates() {
 1080           return duplicates;
 1081       }
 1082   
 1083   
 1084       public void setDuplicates(int duplicates) {
 1085           this.duplicates = duplicates;
 1086       }
 1087   
 1088   
 1089       /** 
 1090        * Returns the number of active sessions
 1091        *
 1092        * @return number of sessions active
 1093        */
 1094       public int getActiveSessions() {
 1095           return sessions.size();
 1096       }
 1097   
 1098   
 1099       /**
 1100        * Max number of concurrent active sessions
 1101        *
 1102        * @return The highest number of concurrent active sessions
 1103        */
 1104       public int getMaxActive() {
 1105           return maxActive;
 1106       }
 1107   
 1108   
 1109       public void setMaxActive(int maxActive) {
 1110           this.maxActive = maxActive;
 1111       }
 1112   
 1113   
 1114       /**
 1115        * Gets the longest time (in seconds) that an expired session had been
 1116        * alive.
 1117        *
 1118        * @return Longest time (in seconds) that an expired session had been
 1119        * alive.
 1120        */
 1121       public int getSessionMaxAliveTime() {
 1122           return sessionMaxAliveTime;
 1123       }
 1124   
 1125   
 1126       /**
 1127        * Sets the longest time (in seconds) that an expired session had been
 1128        * alive.
 1129        *
 1130        * @param sessionMaxAliveTime Longest time (in seconds) that an expired
 1131        * session had been alive.
 1132        */
 1133       public void setSessionMaxAliveTime(int sessionMaxAliveTime) {
 1134           this.sessionMaxAliveTime = sessionMaxAliveTime;
 1135       }
 1136   
 1137   
 1138       /**
 1139        * Gets the average time (in seconds) that expired sessions had been
 1140        * alive.
 1141        *
 1142        * @return Average time (in seconds) that expired sessions had been
 1143        * alive.
 1144        */
 1145       public int getSessionAverageAliveTime() {
 1146           return sessionAverageAliveTime;
 1147       }
 1148   
 1149   
 1150       /**
 1151        * Sets the average time (in seconds) that expired sessions had been
 1152        * alive.
 1153        *
 1154        * @param sessionAverageAliveTime Average time (in seconds) that expired
 1155        * sessions had been alive.
 1156        */
 1157       public void setSessionAverageAliveTime(int sessionAverageAliveTime) {
 1158           this.sessionAverageAliveTime = sessionAverageAliveTime;
 1159       }
 1160   
 1161   
 1162       /** 
 1163        * For debugging: return a list of all session ids currently active
 1164        *
 1165        */
 1166       public String listSessionIds() {
 1167           StringBuffer sb=new StringBuffer();
 1168           Iterator keys = sessions.keySet().iterator();
 1169           while (keys.hasNext()) {
 1170               sb.append(keys.next()).append(" ");
 1171           }
 1172           return sb.toString();
 1173       }
 1174   
 1175   
 1176       /** 
 1177        * For debugging: get a session attribute
 1178        *
 1179        * @param sessionId
 1180        * @param key
 1181        * @return The attribute value, if found, null otherwise
 1182        */
 1183       public String getSessionAttribute( String sessionId, String key ) {
 1184           Session s = (Session) sessions.get(sessionId);
 1185           if( s==null ) {
 1186               if(log.isInfoEnabled())
 1187                   log.info("Session not found " + sessionId);
 1188               return null;
 1189           }
 1190           Object o=s.getSession().getAttribute(key);
 1191           if( o==null ) return null;
 1192           return o.toString();
 1193       }
 1194   
 1195   
 1196       /**
 1197        * Returns information about the session with the given session id.
 1198        * 
 1199        * <p>The session information is organized as a HashMap, mapping 
 1200        * session attribute names to the String representation of their values.
 1201        *
 1202        * @param sessionId Session id
 1203        * 
 1204        * @return HashMap mapping session attribute names to the String
 1205        * representation of their values, or null if no session with the
 1206        * specified id exists, or if the session does not have any attributes
 1207        */
 1208       public HashMap getSession(String sessionId) {
 1209           Session s = (Session) sessions.get(sessionId);
 1210           if (s == null) {
 1211               if (log.isInfoEnabled()) {
 1212                   log.info("Session not found " + sessionId);
 1213               }
 1214               return null;
 1215           }
 1216   
 1217           Enumeration ee = s.getSession().getAttributeNames();
 1218           if (ee == null || !ee.hasMoreElements()) {
 1219               return null;
 1220           }
 1221   
 1222           HashMap map = new HashMap();
 1223           while (ee.hasMoreElements()) {
 1224               String attrName = (String) ee.nextElement();
 1225               map.put(attrName, getSessionAttribute(sessionId, attrName));
 1226           }
 1227   
 1228           return map;
 1229       }
 1230   
 1231   
 1232       public void expireSession( String sessionId ) {
 1233           Session s=(Session)sessions.get(sessionId);
 1234           if( s==null ) {
 1235               if(log.isInfoEnabled())
 1236                   log.info("Session not found " + sessionId);
 1237               return;
 1238           }
 1239           s.expire();
 1240       }
 1241   
 1242       public long getLastAccessedTimestamp( String sessionId ) {
 1243           Session s=(Session)sessions.get(sessionId);
 1244           if(s== null)
 1245               return -1 ;
 1246           return s.getLastAccessedTime();
 1247       }
 1248     
 1249       public String getLastAccessedTime( String sessionId ) {
 1250           Session s=(Session)sessions.get(sessionId);
 1251           if( s==null ) {
 1252               if(log.isInfoEnabled())
 1253                   log.info("Session not found " + sessionId);
 1254               return "";
 1255           }
 1256           return new Date(s.getLastAccessedTime()).toString();
 1257       }
 1258   
 1259       public String getCreationTime( String sessionId ) {
 1260           Session s=(Session)sessions.get(sessionId);
 1261           if( s==null ) {
 1262               if(log.isInfoEnabled())
 1263                   log.info("Session not found " + sessionId);
 1264               return "";
 1265           }
 1266           return new Date(s.getCreationTime()).toString();
 1267       }
 1268   
 1269       public long getCreationTimestamp( String sessionId ) {
 1270           Session s=(Session)sessions.get(sessionId);
 1271           if(s== null)
 1272               return -1 ;
 1273           return s.getCreationTime();
 1274       }
 1275   
 1276       // -------------------- JMX and Registration  --------------------
 1277       protected String domain;
 1278       protected ObjectName oname;
 1279       protected MBeanServer mserver;
 1280   
 1281       public ObjectName getObjectName() {
 1282           return oname;
 1283       }
 1284   
 1285       public String getDomain() {
 1286           return domain;
 1287       }
 1288   
 1289       public ObjectName preRegister(MBeanServer server,
 1290                                     ObjectName name) throws Exception {
 1291           oname=name;
 1292           mserver=server;
 1293           domain=name.getDomain();
 1294           return name;
 1295       }
 1296   
 1297       public void postRegister(Boolean registrationDone) {
 1298       }
 1299   
 1300       public void preDeregister() throws Exception {
 1301       }
 1302   
 1303       public void postDeregister() {
 1304       }
 1305   
 1306   }

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