1 //
2 // ========================================================================
3 // Copyright (c) 1995-2014 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.nosql;
20
21 import java.util.ArrayList;
22 import java.util.concurrent.ConcurrentHashMap;
23 import java.util.concurrent.ConcurrentMap;
24 import java.util.concurrent.TimeUnit;
25
26 import javax.servlet.http.HttpServletRequest;
27
28 import org.eclipse.jetty.server.SessionManager;
29 import org.eclipse.jetty.server.session.AbstractSession;
30 import org.eclipse.jetty.server.session.AbstractSessionManager;
31 import org.eclipse.jetty.util.log.Log;
32 import org.eclipse.jetty.util.log.Logger;
33
34 /**
35 * NoSqlSessionManager
36 *
37 * Base class for SessionManager implementations using nosql frameworks
38 *
39 */
40 public abstract class NoSqlSessionManager extends AbstractSessionManager implements SessionManager
41 {
42 private final static Logger __log = Log.getLogger("org.eclipse.jetty.server.session");
43
44 protected final ConcurrentMap<String,NoSqlSession> _sessions=new ConcurrentHashMap<String,NoSqlSession>();
45
46 private int _stalePeriod=0;
47 private int _savePeriod=0;
48 private int _idlePeriod=-1;
49 private boolean _invalidateOnStop;
50 private boolean _preserveOnStop = true;
51 private boolean _saveAllAttributes;
52
53 /* ------------------------------------------------------------ */
54 /**
55 * @see org.eclipse.jetty.server.session.AbstractSessionManager#doStart()
56 */
57 @Override
58 public void doStart() throws Exception
59 {
60 super.doStart();
61
62 }
63
64 /* ------------------------------------------------------------ */
65 @Override
66 protected void addSession(AbstractSession session)
67 {
68 if (isRunning())
69 {
70 //add into memory
71 _sessions.put(session.getClusterId(),(NoSqlSession)session);
72 //add into db
73 ((NoSqlSession)session).save(true);
74 }
75 }
76
77 /* ------------------------------------------------------------ */
78 @Override
79 public AbstractSession getSession(String idInCluster)
80 {
81 NoSqlSession session = _sessions.get(idInCluster);
82 __log.debug("getSession {} ", session );
83
84 if (session==null)
85 {
86 //session not in this node's memory, load it
87 session=loadSession(idInCluster);
88
89 if (session!=null)
90 {
91 //session exists, check another request thread hasn't loaded it too
92 NoSqlSession race=_sessions.putIfAbsent(idInCluster,session);
93 if (race!=null)
94 {
95 session.willPassivate();
96 session.clearAttributes();
97 session=race;
98 }
99 else
100 __log.debug("session loaded ", idInCluster);
101
102 //check if the session we just loaded has actually expired, maybe while we weren't running
103 if (getMaxInactiveInterval() > 0 && session.getAccessed() > 0 && ((getMaxInactiveInterval()*1000)+session.getAccessed()) < System.currentTimeMillis())
104 {
105 __log.debug("session expired ", idInCluster);
106 expire(idInCluster);
107 session = null;
108 }
109 }
110 else
111 __log.debug("session does not exist {}", idInCluster);
112 }
113
114 return session;
115 }
116
117 /* ------------------------------------------------------------ */
118 @Override
119 protected void shutdownSessions() throws Exception
120 {
121 //If we are stopping, and we're preserving sessions, then we want to
122 //save all of the sessions (including those that have been added during this method call)
123 //and then just remove them from memory.
124
125 //If we don't wish to preserve sessions and we're stopping, then we should invalidate
126 //the session (which may remove it).
127 long gracefulStopMs = getContextHandler().getServer().getStopTimeout();
128 long stopTime = 0;
129 if (gracefulStopMs > 0)
130 stopTime = System.nanoTime() + (TimeUnit.NANOSECONDS.convert(gracefulStopMs, TimeUnit.MILLISECONDS));
131
132 ArrayList<NoSqlSession> sessions=new ArrayList<NoSqlSession>(_sessions.values());
133
134 // loop while there are sessions, and while there is stop time remaining, or if no stop time, just 1 loop
135 while (sessions.size() > 0 && ((stopTime > 0 && (System.nanoTime() < stopTime)) || (stopTime == 0)))
136 {
137 for (NoSqlSession session : sessions)
138 {
139 if (isPreserveOnStop())
140 {
141 //we don't want to delete the session, so save the session
142 //and remove from memory
143 session.save(false);
144 _sessions.remove(session.getClusterId());
145 }
146 else
147 {
148 //invalidate the session so listeners will be called and also removes the session
149 session.invalidate();
150 }
151 }
152
153 //check if we should terminate our loop if we're not using the stop timer
154 if (stopTime == 0)
155 {
156 break;
157 }
158 // Get any sessions that were added by other requests during processing and go around the loop again
159 sessions=new ArrayList<NoSqlSession>(_sessions.values());
160 }
161 }
162
163
164 /* ------------------------------------------------------------ */
165 @Override
166 protected AbstractSession newSession(HttpServletRequest request)
167 {
168 return new NoSqlSession(this,request);
169 }
170
171 /* ------------------------------------------------------------ */
172 /** Remove the session from the in-memory list for this context.
173 * Also remove the context sub-document for this session id from the db.
174 * @see org.eclipse.jetty.server.session.AbstractSessionManager#removeSession(java.lang.String)
175 */
176 @Override
177 protected boolean removeSession(String idInCluster)
178 {
179 synchronized (this)
180 {
181 NoSqlSession session = _sessions.remove(idInCluster);
182
183 try
184 {
185 if (session != null)
186 {
187 return remove(session);
188 }
189 }
190 catch (Exception e)
191 {
192 __log.warn("Problem deleting session {}", idInCluster,e);
193 }
194
195 return session != null;
196 }
197 }
198
199 /* ------------------------------------------------------------ */
200 protected void expire( String idInCluster )
201 {
202 synchronized (this)
203 {
204 //get the session from memory
205 NoSqlSession session = _sessions.get(idInCluster);
206
207 try
208 {
209 if (session == null)
210 {
211 //we need to expire the session with its listeners, so load it
212 session = loadSession(idInCluster);
213 }
214
215 if (session != null)
216 session.timeout();
217 }
218 catch (Exception e)
219 {
220 __log.warn("Problem expiring session {}", idInCluster,e);
221 }
222 }
223 }
224
225
226 public void invalidateSession (String idInCluster)
227 {
228 synchronized (this)
229 {
230 NoSqlSession session = _sessions.get(idInCluster);
231 try
232 {
233 __log.debug("invalidating session {}", idInCluster);
234 if (session != null)
235 {
236 session.invalidate();
237 }
238 }
239 catch (Exception e)
240 {
241 __log.warn("Problem invalidating session {}", idInCluster,e);
242 }
243 }
244 }
245
246
247 /* ------------------------------------------------------------ */
248 /**
249 * The State Period is the maximum time in seconds that an in memory session is allows to be stale:
250 * <ul>
251 * <li>If this period is exceeded, the DB will be checked to see if a more recent version is available.</li>
252 * <li>If the state period is set to a value < 0, then no staleness check will be made.</li>
253 * <li>If the state period is set to 0, then a staleness check is made whenever the active request count goes from 0 to 1.</li>
254 * </ul>
255 * @return the stalePeriod in seconds
256 */
257 public int getStalePeriod()
258 {
259 return _stalePeriod;
260 }
261
262 /* ------------------------------------------------------------ */
263 /**
264 * The State Period is the maximum time in seconds that an in memory session is allows to be stale:
265 * <ul>
266 * <li>If this period is exceeded, the DB will be checked to see if a more recent version is available.</li>
267 * <li>If the state period is set to a value < 0, then no staleness check will be made.</li>
268 * <li>If the state period is set to 0, then a staleness check is made whenever the active request count goes from 0 to 1.</li>
269 * </ul>
270 * @param stalePeriod the stalePeriod in seconds
271 */
272 public void setStalePeriod(int stalePeriod)
273 {
274 _stalePeriod = stalePeriod;
275 }
276
277 /* ------------------------------------------------------------ */
278 /**
279 * The Save Period is the time in seconds between saves of a dirty session to the DB.
280 * When this period is exceeded, the a dirty session will be written to the DB: <ul>
281 * <li>a save period of -2 means the session is written to the DB whenever setAttribute is called.</li>
282 * <li>a save period of -1 means the session is never saved to the DB other than on a shutdown</li>
283 * <li>a save period of 0 means the session is written to the DB whenever the active request count goes from 1 to 0.</li>
284 * <li>a save period of 1 means the session is written to the DB whenever the active request count goes from 1 to 0 and the session is dirty.</li>
285 * <li>a save period of > 1 means the session is written after that period in seconds of being dirty.</li>
286 * </ul>
287 * @return the savePeriod -2,-1,0,1 or the period in seconds >=2
288 */
289 public int getSavePeriod()
290 {
291 return _savePeriod;
292 }
293
294 /* ------------------------------------------------------------ */
295 /**
296 * The Save Period is the time in seconds between saves of a dirty session to the DB.
297 * When this period is exceeded, the a dirty session will be written to the DB: <ul>
298 * <li>a save period of -2 means the session is written to the DB whenever setAttribute is called.</li>
299 * <li>a save period of -1 means the session is never saved to the DB other than on a shutdown</li>
300 * <li>a save period of 0 means the session is written to the DB whenever the active request count goes from 1 to 0.</li>
301 * <li>a save period of 1 means the session is written to the DB whenever the active request count goes from 1 to 0 and the session is dirty.</li>
302 * <li>a save period of > 1 means the session is written after that period in seconds of being dirty.</li>
303 * </ul>
304 * @param savePeriod the savePeriod -2,-1,0,1 or the period in seconds >=2
305 */
306 public void setSavePeriod(int savePeriod)
307 {
308 _savePeriod = savePeriod;
309 }
310
311 /* ------------------------------------------------------------ */
312 /**
313 * The Idle Period is the time in seconds before an in memory session is passivated.
314 * When this period is exceeded, the session will be passivated and removed from memory. If the session was dirty, it will be written to the DB.
315 * If the idle period is set to a value < 0, then the session is never idled.
316 * If the save period is set to 0, then the session is idled whenever the active request count goes from 1 to 0.
317 * @return the idlePeriod
318 */
319 public int getIdlePeriod()
320 {
321 return _idlePeriod;
322 }
323
324 /* ------------------------------------------------------------ */
325 /**
326 * The Idle Period is the time in seconds before an in memory session is passivated.
327 * When this period is exceeded, the session will be passivated and removed from memory. If the session was dirty, it will be written to the DB.
328 * If the idle period is set to a value < 0, then the session is never idled.
329 * If the save period is set to 0, then the session is idled whenever the active request count goes from 1 to 0.
330 * @param idlePeriod the idlePeriod in seconds
331 */
332 public void setIdlePeriod(int idlePeriod)
333 {
334 _idlePeriod = idlePeriod;
335 }
336
337 /* ------------------------------------------------------------ */
338 /**
339 * Invalidate sessions when the session manager is stopped otherwise save them to the DB.
340 * @return the invalidateOnStop
341 */
342 public boolean isInvalidateOnStop()
343 {
344 return _invalidateOnStop;
345 }
346
347 /* ------------------------------------------------------------ */
348 /**
349 * Preserve sessions when the session manager is stopped otherwise remove them from the DB.
350 * @return the removeOnStop
351 */
352 public boolean isPreserveOnStop()
353 {
354 return _preserveOnStop;
355 }
356
357 /* ------------------------------------------------------------ */
358 /**
359 * Invalidate sessions when the session manager is stopped otherwise save them to the DB.
360 * @param invalidateOnStop the invalidateOnStop to set
361 */
362 public void setInvalidateOnStop(boolean invalidateOnStop)
363 {
364 _invalidateOnStop = invalidateOnStop;
365 }
366
367 /* ------------------------------------------------------------ */
368 /**
369 * Preserve sessions when the session manager is stopped otherwise remove them from the DB.
370 * @param removeOnStop the removeOnStop to set
371 */
372 public void setPreserveOnStop(boolean preserveOnStop)
373 {
374 _preserveOnStop = preserveOnStop;
375 }
376
377 /* ------------------------------------------------------------ */
378 /**
379 * Save all attributes of a session or only update the dirty attributes.
380 * @return the saveAllAttributes
381 */
382 public boolean isSaveAllAttributes()
383 {
384 return _saveAllAttributes;
385 }
386
387 /* ------------------------------------------------------------ */
388 /**
389 * Save all attributes of a session or only update the dirty attributes.
390 * @param saveAllAttributes the saveAllAttributes to set
391 */
392 public void setSaveAllAttributes(boolean saveAllAttributes)
393 {
394 _saveAllAttributes = saveAllAttributes;
395 }
396
397 /* ------------------------------------------------------------ */
398 @Override
399 public void renewSessionId(String oldClusterId, String oldNodeId, String newClusterId, String newNodeId)
400 {
401
402 // Take the old session out of the list of sessions
403 // Change to the new id
404 // Put it back into the list of sessions
405 // Update permanent storage
406
407 synchronized (this)
408 {
409 try
410 {
411 NoSqlSession session = _sessions.remove(oldClusterId);
412 update (session, newClusterId, newNodeId);
413 session.setClusterId(newClusterId);
414 session.setNodeId(newNodeId);
415 _sessions.put(newClusterId, session);
416 }
417 catch (Exception e)
418 {
419 __log.warn(e);
420 }
421 }
422 super.renewSessionId(oldClusterId, oldNodeId, newClusterId, newNodeId);
423 }
424
425
426 /* ------------------------------------------------------------ */
427 abstract protected NoSqlSession loadSession(String clusterId);
428
429 /* ------------------------------------------------------------ */
430 abstract protected Object save(NoSqlSession session,Object version, boolean activateAfterSave);
431
432 /* ------------------------------------------------------------ */
433 abstract protected Object refresh(NoSqlSession session, Object version);
434
435 /* ------------------------------------------------------------ */
436 abstract protected boolean remove(NoSqlSession session);
437
438 /* ------------------------------------------------------------ */
439 abstract protected void update(NoSqlSession session, String newClusterId, String newNodeId) throws Exception;
440
441 }