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 }