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 }