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