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