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 }