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

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