View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
4   //  ------------------------------------------------------------------------
5   //  All rights reserved. This program and the accompanying materials
6   //  are made available under the terms of the Eclipse Public License v1.0
7   //  and Apache License v2.0 which accompanies this distribution.
8   //
9   //      The Eclipse Public License is available at
10  //      http://www.eclipse.org/legal/epl-v10.html
11  //
12  //      The Apache License v2.0 is available at
13  //      http://www.opensource.org/licenses/apache2.0.php
14  //
15  //  You may elect to redistribute this code under either of these licenses.
16  //  ========================================================================
17  //
18  
19  package org.eclipse.jetty.server.session;
20  
21  import java.io.DataOutputStream;
22  import java.io.File;
23  import java.io.FileInputStream;
24  import java.io.FileNotFoundException;
25  import java.io.FileOutputStream;
26  import java.io.IOException;
27  import java.io.ObjectOutputStream;
28  import java.io.OutputStream;
29  import java.util.Enumeration;
30  
31  import javax.servlet.http.HttpServletRequest;
32  
33  import org.eclipse.jetty.util.IO;
34  import org.eclipse.jetty.util.log.Log;
35  import org.eclipse.jetty.util.log.Logger;
36  
37  public class HashedSession extends AbstractSession
38  {
39      private static final Logger LOG = Log.getLogger(HashedSession.class);
40  
41      private final HashSessionManager _hashSessionManager;
42  
43      /** Whether the session has been saved because it has been deemed idle;
44       * in which case its attribute map will have been saved and cleared. */
45      private transient boolean _idled = false;
46  
47      /** Whether there has already been an attempt to save this session
48       * which has failed.  If there has, there will be no more save attempts
49       * for this session.  This is to stop the logs being flooded with errors
50       * due to serialization failures that are most likely caused by user
51       * data stored in the session that is not serializable. */
52      private transient boolean _saveFailed = false;
53  
54      /* ------------------------------------------------------------- */
55      protected HashedSession(HashSessionManager hashSessionManager, HttpServletRequest request)
56      {
57          super(hashSessionManager,request);
58          _hashSessionManager = hashSessionManager;
59      }
60  
61      /* ------------------------------------------------------------- */
62      protected HashedSession(HashSessionManager hashSessionManager, long created, long accessed, String clusterId)
63      {
64          super(hashSessionManager,created, accessed, clusterId);
65          _hashSessionManager = hashSessionManager;
66      }
67  
68      /* ------------------------------------------------------------- */
69      protected void checkValid()
70      {
71          if (_hashSessionManager._idleSavePeriodMs!=0)
72              deIdle();
73          super.checkValid();
74      }
75  
76      /* ------------------------------------------------------------- */
77      @Override
78      public void setMaxInactiveInterval(int secs)
79      {
80          super.setMaxInactiveInterval(secs);
81          if (getMaxInactiveInterval()>0&&(getMaxInactiveInterval()*1000L/10)<_hashSessionManager._scavengePeriodMs)
82              _hashSessionManager.setScavengePeriod((secs+9)/10);
83      }
84  
85      /* ------------------------------------------------------------ */
86      @Override
87      protected void doInvalidate()
88      throws IllegalStateException
89      {
90          super.doInvalidate();
91          remove();
92      }
93      
94      
95      /* ------------------------------------------------------------ */
96      /**
97       * Remove from the disk
98       */
99      synchronized void remove ()
100     {
101         if (_hashSessionManager._storeDir!=null && getId()!=null)
102         {
103             String id=getId();
104             File f = new File(_hashSessionManager._storeDir, id);
105             f.delete();
106         }
107     }
108 
109     /* ------------------------------------------------------------ */
110     synchronized void save(boolean reactivate)
111     throws Exception
112     {
113         // Only idle the session if not already idled and no previous save/idle has failed
114         if (!isIdled() && !_saveFailed)
115         {
116             if (LOG.isDebugEnabled())
117                 LOG.debug("Saving {} {}",super.getId(),reactivate);
118 
119             try
120             {
121                 willPassivate();
122                 save();
123                 if (reactivate)
124                     didActivate();
125                 else
126                     clearAttributes();
127             }
128             catch (Exception e)
129             {       
130                 LOG.warn("Problem saving session " + super.getId(), e);
131                 _idled=false; // assume problem was before _values.clear();
132             }
133         }
134     }
135     
136     
137     
138     synchronized void save ()
139     throws Exception
140     {   
141         File file = null;
142         FileOutputStream fos = null;
143         if (!_saveFailed && _hashSessionManager._storeDir != null)
144         {
145             try
146             {
147                 file = new File(_hashSessionManager._storeDir, super.getId());
148                 if (file.exists())
149                     file.delete();
150                 file.createNewFile();
151                 fos = new FileOutputStream(file);
152                 save(fos);
153             }
154             catch (Exception e)
155             {
156                 saveFailed();
157                 if (fos != null)
158                 {
159                     // Must not leave the file open if the saving failed
160                     IO.close(fos);
161                     // No point keeping the file if we didn't save the whole session
162                     file.delete();
163                     throw e;
164                 }
165             }
166         }
167     }
168     
169     
170     /* ------------------------------------------------------------ */
171     public synchronized void save(OutputStream os)  throws IOException
172     {
173         DataOutputStream out = new DataOutputStream(os);
174         out.writeUTF(getClusterId());
175         out.writeUTF(getNodeId());
176         out.writeLong(getCreationTime());
177         out.writeLong(getAccessed());
178 
179         /* Don't write these out, as they don't make sense to store because they
180          * either they cannot be true or their value will be restored in the
181          * Session constructor.
182          */
183         //out.writeBoolean(_invalid);
184         //out.writeBoolean(_doInvalidate);
185         //out.writeLong(_maxIdleMs);
186         //out.writeBoolean( _newSession);
187         out.writeInt(getRequests());
188         out.writeInt(getAttributes());
189         ObjectOutputStream oos = new ObjectOutputStream(out);
190         Enumeration<String> e=getAttributeNames();
191         while(e.hasMoreElements())
192         {
193             String key=e.nextElement();
194             oos.writeUTF(key);
195             oos.writeObject(doGet(key));
196         }
197         oos.close();
198     }
199 
200     /* ------------------------------------------------------------ */
201     public synchronized void deIdle()
202     {
203         if (isIdled())
204         {
205             // Access now to prevent race with idling period
206             access(System.currentTimeMillis());
207 
208             if (LOG.isDebugEnabled())
209                 LOG.debug("De-idling " + super.getId());
210 
211             FileInputStream fis = null;
212 
213             try
214             {
215                 File file = new File(_hashSessionManager._storeDir, super.getId());
216                 if (!file.exists() || !file.canRead())
217                     throw new FileNotFoundException(file.getName());
218 
219                 fis = new FileInputStream(file);
220                 _idled = false;
221                 _hashSessionManager.restoreSession(fis, this);
222 
223                 didActivate();
224 
225                 // If we are doing period saves, then there is no point deleting at this point
226                 if (_hashSessionManager._savePeriodMs == 0)
227                     file.delete();
228             }
229             catch (Exception e)
230             {
231                 LOG.warn("Problem de-idling session " + super.getId(), e);
232                 IO.close(fis);
233                 invalidate();
234             }
235         }
236     }
237 
238 
239     /* ------------------------------------------------------------ */
240     /**
241      * Idle the session to reduce session memory footprint.
242      *
243      * The session is idled by persisting it, then clearing the session values attribute map and finally setting
244      * it to an idled state.
245      */
246     public synchronized void idle()
247     throws Exception
248     {
249         save(false);
250         _idled = true;
251     }
252 
253     /* ------------------------------------------------------------ */
254     public synchronized boolean isIdled()
255     {
256       return _idled;
257     }
258 
259     /* ------------------------------------------------------------ */
260     public synchronized boolean isSaveFailed()
261     {
262         return _saveFailed;
263     }
264 
265     /* ------------------------------------------------------------ */
266     public synchronized void saveFailed()
267     {
268         _saveFailed = true;
269     }
270 
271 }