Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
   //
   //  ========================================================================
   //  Copyright (c) 1995-2012 Mort Bay Consulting Pty. Ltd.
   //  ------------------------------------------------------------------------
   //  All rights reserved. This program and the accompanying materials
   //  are made available under the terms of the Eclipse Public License v1.0
   //  and Apache License v2.0 which accompanies this distribution.
   //
   //      The Eclipse Public License is available at
  //      http://www.eclipse.org/legal/epl-v10.html
  //
  //      The Apache License v2.0 is available at
  //      http://www.opensource.org/licenses/apache2.0.php
  //
  //  You may elect to redistribute this code under either of these licenses.
  //  ========================================================================
  //
  
  
  package org.eclipse.jetty.server.session;
  
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  import java.util.Set;
  
  
JDBCSessionManager SessionManager that persists sessions to a database to enable clustering. Session data is persisted to the JettySessions table: rowId (unique in cluster: webapp name/path + virtualhost + sessionId) contextPath (of the context owning the session) sessionId (unique in a context) lastNode (name of node last handled session) accessTime (time in milliseconds session was accessed) lastAccessTime (previous time in milliseconds session was accessed) createTime (time in milliseconds session created) cookieTime (time in milliseconds session cookie created) lastSavedTime (last time in milliseconds session access times were saved) expiryTime (time in milliseconds that the session is due to expire) map (attribute map) As an optimization, to prevent thrashing the database, we do not persist the accessTime and lastAccessTime every time the session is accessed. Rather, we write it out every so often. The frequency is controlled by the saveIntervalSec field.
  
  public class JDBCSessionManager extends AbstractSessionManager
  {
      private static final Logger LOG = Log.getLogger(JDBCSessionManager.class);
  
      protected JDBCSessionIdManager _jdbcSessionIdMgr = null;
      protected long _saveIntervalSec = 60; //only persist changes to session access times every 60 secs
  
    
SessionData Persistable data about a session.
  
      public class SessionData
      {
          private final String _id;
          private String _rowId;
          private long _accessed;
          private long _lastAccessed;
          private long _maxIdleMs=-1;
          private long _cookieSet;
          private long _created;
          private Map<String,Object_attributes;
          private String _lastNode;
          private String _canonicalContext;
         private long _lastSaved;
         private long _expiryTime;
         private String _virtualHost;
 
         public SessionData (String sessionId)
         {
             =sessionId;
             =System.currentTimeMillis();
              = ;
              = new HashMap<String,Object>();
              = getSessionIdManager().getWorkerName();
         }
 
         public SessionData (String sessionId,Map<String,Objectattributes)
         {
             =sessionId;
             =System.currentTimeMillis();
              = ;
              = attributes;
              = getSessionIdManager().getWorkerName();
         }
 
         public synchronized String getId ()
         {
             return ;
         }
 
         public synchronized long getCreated ()
         {
             return ;
         }
 
         protected synchronized void setCreated (long ms)
         {
              = ms;
         }
 
         public synchronized long getAccessed ()
         {
             return ;
         }
 
         protected synchronized void setAccessed (long ms)
         {
              = ms;
         }
 
 
         public synchronized void setMaxIdleMs (long ms)
         {
              = ms;
         }
 
         public synchronized long getMaxIdleMs()
         {
             return ;
         }
 
         public synchronized void setLastAccessed (long ms)
         {
              = ms;
         }
 
         public synchronized long getLastAccessed()
         {
             return ;
         }
 
         public void setCookieSet (long ms)
         {
              = ms;
         }
 
         public synchronized long getCookieSet ()
         {
             return ;
         }
 
         public synchronized void setRowId (String rowId)
         {
             =rowId;
         }
 
         protected synchronized String getRowId()
         {
             return ;
         }
 
         protected synchronized Map<String,ObjectgetAttributeMap ()
         {
             return ;
         }
 
         protected synchronized void setAttributeMap (Map<String,Objectmap)
         {
              = map;
         }
 
         public synchronized void setLastNode (String node)
         {
             =node;
         }
 
         public synchronized String getLastNode ()
         {
             return ;
         }
 
         public synchronized void setCanonicalContext(String str)
         {
             =str;
         }
 
         public synchronized String getCanonicalContext ()
         {
             return ;
         }
 
         public synchronized long getLastSaved ()
         {
             return ;
         }
 
         public synchronized void setLastSaved (long time)
         {
             =time;
         }
 
         public synchronized void setExpiryTime (long time)
         {
             =time;
         }
 
         public synchronized long getExpiryTime ()
         {
             return ;
         }
 
         public synchronized void setVirtualHost (String vhost)
         {
             =vhost;
         }
 
         public synchronized String getVirtualHost ()
         {
             return ;
         }
 
         @Override
         public String toString ()
         {
             return "Session rowId="++",id="++",lastNode="++
                             ",created="++",accessed="++
                             ",lastAccessed="++",cookieSet="++
                             "lastSaved="+;
         }
     }



    
Session Session instance in memory of this node.
 
     public class Session extends AbstractSession
     {
         private static final long serialVersionUID = 5208464051134226143L;
         private final SessionData _data;
         private boolean _dirty=false;
       
        
        
Session from a request.

Parameters:
request
 
         protected Session (HttpServletRequest request)
         {
             super(JDBCSessionManager.this,request);
              = new SessionData(getClusterId(),getAttributeMap());
             if (>0)
                 .setMaxIdleMs(*1000L);
             .setVirtualHost(getVirtualHost());
             int maxInterval=getMaxInactiveInterval();
             .setExpiryTime(maxInterval <= 0 ? 0 : (System.currentTimeMillis() + maxInterval*1000L));
         }

        
Session restored in database.

Parameters:
data
 
         protected Session (long accessedSessionData data)
         {
             super(JDBCSessionManager.this,data.getCreated(), accesseddata.getId());
             =data;
             if (>0)
                 .setMaxIdleMs(*1000L);
             addAttributes(.getAttributeMap());
             .setAttributeMap(getAttributeMap());
         }
 
          @Override
         public void setAttribute (String nameObject value)
          {
              super.setAttribute(namevalue);
              =true;
          }
 
          @Override
         public void removeAttribute (String name)
          {
              super.removeAttribute(name);
              =true;
          }
 
          @Override
         protected void cookieSet()
          {
              .setCookieSet(.getAccessed());
          }

        
Entry to session. Called by SessionHandler on inbound request and the session already exists in this node's memory.

 
         @Override
         protected boolean access(long time)
         {
             if (super.access(time))
             {
                 .setLastAccessed(.getAccessed());
                 .setAccessed(time);
 
                 int maxInterval=getMaxInactiveInterval();
                 .setExpiryTime(maxInterval <= 0 ? 0 : (time + maxInterval*1000L));
                 return true;
             }
             return false;
         }

        
Exit from session

 
         @Override
         protected void complete()
         {
             super.complete();
             try
             {
                 if ()
                 {
                     //The session attributes have changed, write to the db, ensuring
                     //http passivation/activation listeners called
                     willPassivate();
                     updateSession();
                     didActivate();
                 }
                 else if ((. - .) >= (getSaveInterval() * 1000L))
                 {
                     updateSessionAccessTime();
                 }
             }
             catch (Exception e)
             {
                 .warn("Problem persisting changed session data id="+getId(), e);
             }
             finally
             {
                 =false;
             }
         }
 
         @Override
         protected void timeout() throws IllegalStateException
         {
             if (.isDebugEnabled())
                 .debug("Timing out session id="+getClusterId());
             super.timeout();
         }
     }




    
ClassLoadingObjectInputStream
 
     protected class ClassLoadingObjectInputStream extends ObjectInputStream
     {
         public ClassLoadingObjectInputStream(java.io.InputStream inthrows IOException
         {
             super(in);
         }
 
         public ClassLoadingObjectInputStream () throws IOException
         {
             super();
         }
 
         @Override
         public Class<?> resolveClass (java.io.ObjectStreamClass clthrows IOExceptionClassNotFoundException
         {
             try
             {
                 return Class.forName(cl.getName(), false, Thread.currentThread().getContextClassLoader());
             }
             catch (ClassNotFoundException e)
             {
                 return super.resolveClass(cl);
             }
         }
     }




    
Set the time in seconds which is the interval between saving the session access time to the database. This is an optimization that prevents the database from being overloaded when a session is accessed very frequently. On session exit, if the session attributes have NOT changed, the time at which we last saved the accessed time is compared to the current accessed time. If the interval is at least saveIntervalSecs, then the access time will be persisted to the database. If any session attribute does change, then the attributes and the accessed time are persisted.

Parameters:
sec
 
     public void setSaveInterval (long sec)
     {
         =sec;
     }
 
     public long getSaveInterval ()
     {
         return ;
     }



    
A method that can be implemented in subclasses to support distributed caching of sessions. This method will be called whenever the session is written to the database because the session data has changed. This could be used eg with a JMS backplane to notify nodes that the session has changed and to delete the session from the node's cache, and re-read it from the database.

Parameters:
session
 
     public void cacheInvalidate (Session session)
     {
 
     }


    
A session has been requested by it's id on this node. Load the session by id AND context path from the database. Multiple contexts may share the same session id (due to dispatching) but they CANNOT share the same contents. Check if last node id is my node id, if so, then the session we have in memory cannot be stale. If another node used the session last, then we need to refresh from the db. NOTE: this method will go to the database, so if you only want to check for the existence of a Session in memory, use _sessions.get(id) instead.

 
     @Override
     public Session getSession(String idInCluster)
     {
         Session session = (Session).get(idInCluster);
 
         synchronized (this)
         {
             try
             {
                 //check if we need to reload the session -
                 //as an optimization, don't reload on every access
                 //to reduce the load on the database. This introduces a window of
                 //possibility that the node may decide that the session is local to it,
                 //when the session has actually been live on another node, and then
                 //re-migrated to this node. This should be an extremely rare occurrence,
                 //as load-balancers are generally well-behaved and consistently send
                 //sessions to the same node, changing only iff that node fails.
                 SessionData data = null;
                 long now = System.currentTimeMillis();
                 if (.isDebugEnabled())
                 {
                     if (session==null)
                         .debug("getSession("+idInCluster+"): not in session map,"+
                                 " now="+now+
                                 " lastSaved="+(session==null?0:session._data._lastSaved)+
                                 " interval="+( * 1000L));
                     else
                         .debug("getSession("+idInCluster+"): in session map, "+
                                 " now="+now+
                                 " lastSaved="+(session==null?0:session._data._lastSaved)+
                                 " interval="+( * 1000L)+
                                 " lastNode="+session._data.getLastNode()+
                                 " thisNode="+getSessionIdManager().getWorkerName()+
                                 " difference="+(now - session._data._lastSaved));
                 }
 
                 if (session==null || ((now - session._data._lastSaved) >= ( * 1000L)))
                 {
                     .debug("getSession("+idInCluster+"): no session in session map or stale session. Reloading session data from db.");
                     data = loadSession(idInClustercanonicalize(.getContextPath()), getVirtualHost());
                 }
                 else if ((now - session._data._lastSaved) >= ( * 1000L))
                 {
                     .debug("getSession("+idInCluster+"): stale session. Reloading session data from db.");
                     data = loadSession(idInClustercanonicalize(.getContextPath()), getVirtualHost());
                 }
                 else
                 {
                     .debug("getSession("+idInCluster+"): session in session map");
                     data = session._data;
                 }
 
                 if (data != null)
                 {
                     if (!data.getLastNode().equals(getSessionIdManager().getWorkerName()) || session==null)
                     {
                         //if the session has no expiry, or it is not already expired
                         if (data._expiryTime <= 0 || data._expiryTime > now)
                         {
                             if (.isDebugEnabled()) .debug("getSession("+idInCluster+"): lastNode="+data.getLastNode()+" thisNode="+getSessionIdManager().getWorkerName());
                             data.setLastNode(getSessionIdManager().getWorkerName());
                             //session last used on a different node, or we don't have it in memory
                             session = new Session(now,data);
                             .put(idInClustersession);
                             session.didActivate();
                             //TODO is this the best way to do this? Or do this on the way out using
                             //the _dirty flag?
                             updateSessionNode(data);
                         }
                         else
                         {
                             .debug("getSession ({}): Session has expired"idInCluster);
                             
                         }
 
                     }
                     else
                        .debug("getSession({}): Session not stale {}"idInCluster,session._data);
                     //session in db shares same id, but is not for this context
                 }
                 else
                 {
                     //No session in db with matching id and context path.
                     session=null;
                     .debug("getSession({}): No session in database matching id={}",idInCluster,idInCluster);
                 }
 
                 return session;
             }
             catch (Exception e)
             {
                 .warn("Unable to load session from database"e);
                 return null;
             }
         }
     }

    
Get the number of sessions.

 
     @Override
     public int getSessions()
     {
         int size = 0;
         synchronized (this)
         {
             size = .size();
         }
         return size;
     }


    
Start the session manager.

 
     @Override
     public void doStart() throws Exception
     {
         if (==null)
             throw new IllegalStateException("No session id manager defined");
 
         
          = new ConcurrentHashMap<StringAbstractSession>();
 
         super.doStart();
     }


    
Stop the session manager.

 
     @Override
     public void doStop() throws Exception
     {
         .clear();
          = null;
 
         super.doStop();
     }
 
     @Override
     protected void invalidateSessions()
     {
         //Do nothing - we don't want to remove and
         //invalidate all the sessions because this
         //method is called from doStop(), and just
         //because this context is stopping does not
         //mean that we should remove the session from
         //any other nodes
     }


    
Invalidate a session.

Parameters:
idInCluster
 
     protected void invalidateSession (String idInCluster)
     {
         Session session = null;
         synchronized (this)
         {
             session = (Session).get(idInCluster);
         }
 
         if (session != null)
         {
             session.invalidate();
         }
     }

    
Delete an existing session, both from the in-memory map and the database.

 
     @Override
     protected boolean removeSession(String idInCluster)
     {
         synchronized (this)
         {
             Session session = (Session).remove(idInCluster);
             try
             {
                 if (session != null)
                     deleteSession(session._data);
             }
             catch (Exception e)
             {
                 .warn("Problem deleting session id="+idInClustere);
             }
             return session!=null;
         }
     }


    
Add a newly created session to our in-memory list for this node and persist it.

 
     @Override
     protected void addSession(AbstractSession session)
     {
         if (session==null)
             return;
 
         synchronized (this)
         {
             .put(session.getClusterId(), session);
         }
 
         //TODO or delay the store until exit out of session? If we crash before we store it
         //then session data will be lost.
         try
         {
             session.willPassivate();
             storeSession(((JDBCSessionManager.Session)session).);
             session.didActivate();
         }
         catch (Exception e)
         {
             .warn("Unable to store new session id="+session.getId() , e);
         }
     }


    
 
     @Override
     protected AbstractSession newSession(HttpServletRequest request)
     {
         return new Session(request);
     }
 
     /* ------------------------------------------------------------ */
    
Remove session from manager

 
     @Override
     public void removeSession(AbstractSession sessionboolean invalidate)
     {
         // Remove session from context and global maps
         boolean removed = false;
 
         synchronized (this)
         {
             //take this session out of the map of sessions for this context
             if (getSession(session.getClusterId()) != null)
             {
                 removed = true;
                 removeSession(session.getClusterId());
             }
         }
 
         if (removed)
         {
             // Remove session from all context and global id maps
             .removeSession(session);
 
             if (invalidate)
                 .invalidateAll(session.getClusterId());
 
             if (invalidate && !.isEmpty())
             {
                 HttpSessionEvent event=new HttpSessionEvent(session);
                 for (HttpSessionListener l : )
                     l.sessionDestroyed(event);
             }
             if (!invalidate)
             {
                 session.willPassivate();
             }
         }
     }


    
Expire any Sessions we have in memory matching the list of expired Session ids.

Parameters:
sessionIds
 
     protected void expire (List<?> sessionIds)
     {
         //don't attempt to scavenge if we are shutting down
         if (isStopping() || isStopped())
             return;
 
         //Remove any sessions we already have in memory that match the ids
         Thread thread=Thread.currentThread();
         ClassLoader old_loader=thread.getContextClassLoader();
         ListIterator<?> itor = sessionIds.listIterator();
 
         try
         {
             while (itor.hasNext())
             {
                 String sessionId = (String)itor.next();
                 if (.isDebugEnabled())
                     .debug("Expiring session id "+sessionId);
 
                 Session session = (Session).get(sessionId);
                 if (session != null)
                 {
                     session.timeout();
                     itor.remove();
                 }
                 else
                 {
                     if (.isDebugEnabled())
                         .debug("Unrecognized session id="+sessionId);
                 }
             }
         }
         catch (Throwable t)
         {
             .warn("Problem expiring sessions"t);
         }
         finally
         {
             thread.setContextClassLoader(old_loader);
         }
     }


    
Load a session from the database

Parameters:
id
Returns:
the session data that was loaded
Throws:
java.lang.Exception
 
     protected SessionData loadSession (final String idfinal String canonicalContextPathfinal String vhost)
     throws Exception
     {
         final AtomicReference<SessionData_reference = new AtomicReference<SessionData>();
         final AtomicReference<Exception_exception = new AtomicReference<Exception>();
         Runnable load = new Runnable()
         {
             @SuppressWarnings("unchecked")
             public void run()
             {
                 SessionData data = null;
                 Connection connection=null;
                 PreparedStatement statement = null;
                 try
                 {
                     connection = getConnection();
                     statement = ..getLoadStatement(connectionidcanonicalContextPathvhost);
                     ResultSet result = statement.executeQuery();
                     if (result.next())
                     {
                         data = new SessionData(id);
                         data.setRowId(result.getString(.));
                         data.setCookieSet(result.getLong("cookieTime"));
                         data.setLastAccessed(result.getLong("lastAccessTime"));
                         data.setAccessed (result.getLong("accessTime"));
                         data.setCreated(result.getLong("createTime"));
                         data.setLastNode(result.getString("lastNode"));
                         data.setLastSaved(result.getLong("lastSavedTime"));
                         data.setExpiryTime(result.getLong("expiryTime"));
                         data.setCanonicalContext(result.getString("contextPath"));
                         data.setVirtualHost(result.getString("virtualHost"));
 
                         InputStream is = ((JDBCSessionIdManager)getSessionIdManager())..getBlobInputStream(result"map");
                         ClassLoadingObjectInputStream ois = new ClassLoadingObjectInputStream (is);
                         Object o = ois.readObject();
                         data.setAttributeMap((Map<String,Object>)o);
                         ois.close();
 
                         if (.isDebugEnabled())
                             .debug("LOADED session "+data);
                     }
                     _reference.set(data);
                 }
                 catch (Exception e)
                 {
                     _exception.set(e);
                 }
                 finally
                 {
                     if (connection!=null)
                     {
                         try { connection.close();}
                         catch(Exception e) { .warn(e); }
                     }
                 }
             }
         };
 
         if (==null)
             load.run();
         else
             .getContextHandler().handle(load);
 
         if (_exception.get()!=null)
             throw _exception.get();
 
         return _reference.get();
     }

    
Insert a session into the database.

Parameters:
data
Throws:
java.lang.Exception
 
     protected void storeSession (SessionData data)
     throws Exception
     {
         if (data==null)
             return;
 
         //put into the database
         Connection connection = getConnection();
         PreparedStatement statement = null;
         try
         {
             String rowId = calculateRowId(data);
 
             long now = System.currentTimeMillis();
             connection.setAutoCommit(true);
             statement = connection.prepareStatement(.);
             statement.setString(1, rowId); //rowId
             statement.setString(2, data.getId()); //session id
             statement.setString(3, data.getCanonicalContext()); //context path
             statement.setString(4, data.getVirtualHost()); //first vhost
             statement.setString(5, getSessionIdManager().getWorkerName());//my node id
             statement.setLong(6, data.getAccessed());//accessTime
             statement.setLong(7, data.getLastAccessed()); //lastAccessTime
             statement.setLong(8, data.getCreated()); //time created
             statement.setLong(9, data.getCookieSet());//time cookie was set
             statement.setLong(10, now); //last saved time
             statement.setLong(11, data.getExpiryTime());
 
             ByteArrayOutputStream baos = new ByteArrayOutputStream();
             ObjectOutputStream oos = new ObjectOutputStream(baos);
             oos.writeObject(data.getAttributeMap());
             byte[] bytes = baos.toByteArray();
 
             ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
             statement.setBinaryStream(12, baisbytes.length);//attribute map as blob
 
             statement.executeUpdate();
             data.setRowId(rowId); //set it on the in-memory data as well as in db
             data.setLastSaved(now);
 
 
             if (.isDebugEnabled())
                 .debug("Stored session "+data);
         }
         finally
         {
             if (connection!=null)
                 connection.close();
         }
     }


    
Update data on an existing persisted session.

Parameters:
data
Throws:
java.lang.Exception
 
     protected void updateSession (SessionData data)
     throws Exception
     {
         if (data==null)
             return;
 
         Connection connection = getConnection();
         PreparedStatement statement = null;
         try
         {
             long now = System.currentTimeMillis();
             connection.setAutoCommit(true);
             statement = connection.prepareStatement(.);
             statement.setString(1, getSessionIdManager().getWorkerName());//my node id
             statement.setLong(2, data.getAccessed());//accessTime
             statement.setLong(3, data.getLastAccessed()); //lastAccessTime
             statement.setLong(4, now); //last saved time
             statement.setLong(5, data.getExpiryTime());
 
             ByteArrayOutputStream baos = new ByteArrayOutputStream();
             ObjectOutputStream oos = new ObjectOutputStream(baos);
             oos.writeObject(data.getAttributeMap());
             byte[] bytes = baos.toByteArray();
             ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
 
             statement.setBinaryStream(6, baisbytes.length);//attribute map as blob
             statement.setString(7, data.getRowId()); //rowId
             statement.executeUpdate();
 
             data.setLastSaved(now);
             if (.isDebugEnabled())
                 .debug("Updated session "+data);
         }
         finally
         {
             if (connection!=null)
                connection.close();
        }
    }


    
Update the node on which the session was last seen to be my node.

Parameters:
data
Throws:
java.lang.Exception
    protected void updateSessionNode (SessionData data)
    throws Exception
    {
        String nodeId = getSessionIdManager().getWorkerName();
        Connection connection = getConnection();
        PreparedStatement statement = null;
        try
        {
            connection.setAutoCommit(true);
            statement = connection.prepareStatement(.);
            statement.setString(1, nodeId);
            statement.setString(2, data.getRowId());
            statement.executeUpdate();
            statement.close();
            if (.isDebugEnabled())
                .debug("Updated last node for session id="+data.getId()+", lastNode = "+nodeId);
        }
        finally
        {
            if (connection!=null)
                connection.close();
        }
    }

    
Persist the time the session was last accessed.

Parameters:
data
Throws:
java.lang.Exception
    private void updateSessionAccessTime (SessionData data)
    throws Exception
    {
        Connection connection = getConnection();
        PreparedStatement statement = null;
        try
        {
            long now = System.currentTimeMillis();
            connection.setAutoCommit(true);
            statement = connection.prepareStatement(.);
            statement.setString(1, getSessionIdManager().getWorkerName());
            statement.setLong(2, data.getAccessed());
            statement.setLong(3, data.getLastAccessed());
            statement.setLong(4, now);
            statement.setLong(5, data.getExpiryTime());
            statement.setString(6, data.getRowId());
            statement.executeUpdate();
            data.setLastSaved(now);
            statement.close();
            if (.isDebugEnabled())
                .debug("Updated access time session id="+data.getId());
        }
        finally
        {
            if (connection!=null)
                connection.close();
        }
    }




    
Delete a session from the database. Should only be called when the session has been invalidated.

Parameters:
data
Throws:
java.lang.Exception
    protected void deleteSession (SessionData data)
    throws Exception
    {
        Connection connection = getConnection();
        PreparedStatement statement = null;
        try
        {
            connection.setAutoCommit(true);
            statement = connection.prepareStatement(.);
            statement.setString(1, data.getRowId());
            statement.executeUpdate();
            if (.isDebugEnabled())
                .debug("Deleted Session "+data);
        }
        finally
        {
            if (connection!=null)
                connection.close();
        }
    }



    
Get a connection from the driver.

Returns:
Throws:
java.sql.SQLException
    private Connection getConnection ()
    throws SQLException
    {
    }

    
Calculate a unique id for this session across the cluster. Unique id is composed of: contextpath_virtualhost0_sessionid

Parameters:
data
Returns:
    private String calculateRowId (SessionData data)
    {
        String rowId = canonicalize(.getContextPath());
        rowId = rowId + "_" + getVirtualHost();
        rowId = rowId+"_"+data.getId();
        return rowId;
    }

    
Get the first virtual host for the context. Used to help identify the exact session/contextPath.

Returns:
0.0.0.0 if no virtual host is defined
    private String getVirtualHost (ContextHandler.Context context)
    {
        String vhost = "0.0.0.0";
        if (context==null)
            return vhost;
        String [] vhosts = context.getContextHandler().getVirtualHosts();
        if (vhosts==null || vhosts.length==0 || vhosts[0]==null)
            return vhost;
        return vhosts[0];
    }

    
Make an acceptable file name from a context path.

Parameters:
path
Returns:
    private String canonicalize (String path)
    {
        if (path==null)
            return "";
        return path.replace('/''_').replace('.','_').replace('\\','_');
    }