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()*1000L)+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 NoSqlSession session = _sessions.remove(idInCluster);
180
181 try
182 {
183 if (session != null)
184 {
185 return remove(session);
186 }
187 }
188 catch (Exception e)
189 {
190 __log.warn("Problem deleting session {}", idInCluster,e);
191 }
192
193 return session != null;
194
195 }
196
197 /* ------------------------------------------------------------ */
198 protected void expire( String idInCluster )
199 {
200 //get the session from memory
201 NoSqlSession session = _sessions.get(idInCluster);
202
203 try
204 {
205 if (session == null)
206 {
207 //we need to expire the session with its listeners, so load it
208 session = loadSession(idInCluster);
209 }
210
211 if (session != null)
212 session.timeout();
213 }
214 catch (Exception e)
215 {
216 __log.warn("Problem expiring session {}", idInCluster,e);
217 }
218 }
219
220
221 public void invalidateSession (String idInCluster)
222 {
223 NoSqlSession session = _sessions.get(idInCluster);
224 try
225 {
226 __log.debug("invalidating session {}", idInCluster);
227 if (session != null)
228 {
229 session.invalidate();
230 }
231 }
232 catch (Exception e)
233 {
234 __log.warn("Problem invalidating session {}", idInCluster,e);
235 }
236 }
237
238
239 /* ------------------------------------------------------------ */
240 /**
241 * The State Period is the maximum time in seconds that an in memory session is allows to be stale:
242 * <ul>
243 * <li>If this period is exceeded, the DB will be checked to see if a more recent version is available.</li>
244 * <li>If the state period is set to a value < 0, then no staleness check will be made.</li>
245 * <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>
246 * </ul>
247 * @return the stalePeriod in seconds
248 */
249 public int getStalePeriod()
250 {
251 return _stalePeriod;
252 }
253
254 /* ------------------------------------------------------------ */
255 /**
256 * The State Period is the maximum time in seconds that an in memory session is allows to be stale:
257 * <ul>
258 * <li>If this period is exceeded, the DB will be checked to see if a more recent version is available.</li>
259 * <li>If the state period is set to a value < 0, then no staleness check will be made.</li>
260 * <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>
261 * </ul>
262 * @param stalePeriod the stalePeriod in seconds
263 */
264 public void setStalePeriod(int stalePeriod)
265 {
266 _stalePeriod = stalePeriod;
267 }
268
269 /* ------------------------------------------------------------ */
270 /**
271 * The Save Period is the time in seconds between saves of a dirty session to the DB.
272 * When this period is exceeded, the a dirty session will be written to the DB: <ul>
273 * <li>a save period of -2 means the session is written to the DB whenever setAttribute is called.</li>
274 * <li>a save period of -1 means the session is never saved to the DB other than on a shutdown</li>
275 * <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>
276 * <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>
277 * <li>a save period of > 1 means the session is written after that period in seconds of being dirty.</li>
278 * </ul>
279 * @return the savePeriod -2,-1,0,1 or the period in seconds >=2
280 */
281 public int getSavePeriod()
282 {
283 return _savePeriod;
284 }
285
286 /* ------------------------------------------------------------ */
287 /**
288 * The Save Period is the time in seconds between saves of a dirty session to the DB.
289 * When this period is exceeded, the a dirty session will be written to the DB: <ul>
290 * <li>a save period of -2 means the session is written to the DB whenever setAttribute is called.</li>
291 * <li>a save period of -1 means the session is never saved to the DB other than on a shutdown</li>
292 * <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>
293 * <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>
294 * <li>a save period of > 1 means the session is written after that period in seconds of being dirty.</li>
295 * </ul>
296 * @param savePeriod the savePeriod -2,-1,0,1 or the period in seconds >=2
297 */
298 public void setSavePeriod(int savePeriod)
299 {
300 _savePeriod = savePeriod;
301 }
302
303 /* ------------------------------------------------------------ */
304 /**
305 * The Idle Period is the time in seconds before an in memory session is passivated.
306 * 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.
307 * If the idle period is set to a value < 0, then the session is never idled.
308 * If the save period is set to 0, then the session is idled whenever the active request count goes from 1 to 0.
309 * @return the idlePeriod
310 */
311 public int getIdlePeriod()
312 {
313 return _idlePeriod;
314 }
315
316 /* ------------------------------------------------------------ */
317 /**
318 * The Idle Period is the time in seconds before an in memory session is passivated.
319 * 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.
320 * If the idle period is set to a value < 0, then the session is never idled.
321 * If the save period is set to 0, then the session is idled whenever the active request count goes from 1 to 0.
322 * @param idlePeriod the idlePeriod in seconds
323 */
324 public void setIdlePeriod(int idlePeriod)
325 {
326 _idlePeriod = idlePeriod;
327 }
328
329 /* ------------------------------------------------------------ */
330 /**
331 * Invalidate sessions when the session manager is stopped otherwise save them to the DB.
332 * @return the invalidateOnStop
333 */
334 public boolean isInvalidateOnStop()
335 {
336 return _invalidateOnStop;
337 }
338
339 /* ------------------------------------------------------------ */
340 /**
341 * Preserve sessions when the session manager is stopped otherwise remove them from the DB.
342 * @return the removeOnStop
343 */
344 public boolean isPreserveOnStop()
345 {
346 return _preserveOnStop;
347 }
348
349 /* ------------------------------------------------------------ */
350 /**
351 * Invalidate sessions when the session manager is stopped otherwise save them to the DB.
352 * @param invalidateOnStop the invalidateOnStop to set
353 */
354 public void setInvalidateOnStop(boolean invalidateOnStop)
355 {
356 _invalidateOnStop = invalidateOnStop;
357 }
358
359 /* ------------------------------------------------------------ */
360 /**
361 * Preserve sessions when the session manager is stopped otherwise remove them from the DB.
362 * @param removeOnStop the removeOnStop to set
363 */
364 public void setPreserveOnStop(boolean preserveOnStop)
365 {
366 _preserveOnStop = preserveOnStop;
367 }
368
369 /* ------------------------------------------------------------ */
370 /**
371 * Save all attributes of a session or only update the dirty attributes.
372 * @return the saveAllAttributes
373 */
374 public boolean isSaveAllAttributes()
375 {
376 return _saveAllAttributes;
377 }
378
379 /* ------------------------------------------------------------ */
380 /**
381 * Save all attributes of a session or only update the dirty attributes.
382 * @param saveAllAttributes the saveAllAttributes to set
383 */
384 public void setSaveAllAttributes(boolean saveAllAttributes)
385 {
386 _saveAllAttributes = saveAllAttributes;
387 }
388
389 /* ------------------------------------------------------------ */
390 @Override
391 public void renewSessionId(String oldClusterId, String oldNodeId, String newClusterId, String newNodeId)
392 {
393
394 // Take the old session out of the list of sessions
395 // Change to the new id
396 // Put it back into the list of sessions
397 // Update permanent storage
398
399 synchronized (this)
400 {
401 try
402 {
403 NoSqlSession session = _sessions.remove(oldClusterId);
404 update (session, newClusterId, newNodeId);
405 session.setClusterId(newClusterId);
406 session.setNodeId(newNodeId);
407 _sessions.put(newClusterId, session);
408 }
409 catch (Exception e)
410 {
411 __log.warn(e);
412 }
413 }
414 super.renewSessionId(oldClusterId, oldNodeId, newClusterId, newNodeId);
415 }
416
417
418 /* ------------------------------------------------------------ */
419 abstract protected NoSqlSession loadSession(String clusterId);
420
421 /* ------------------------------------------------------------ */
422 abstract protected Object save(NoSqlSession session,Object version, boolean activateAfterSave);
423
424 /* ------------------------------------------------------------ */
425 abstract protected Object refresh(NoSqlSession session, Object version);
426
427 /* ------------------------------------------------------------ */
428 abstract protected boolean remove(NoSqlSession session);
429
430 /* ------------------------------------------------------------ */
431 abstract protected void update(NoSqlSession session, String newClusterId, String newNodeId) throws Exception;
432
433 }