1 5 package com.terracotta.session; 6 7 import com.tc.logging.TCLogger; 8 import com.tc.management.beans.sessions.SessionMonitorMBean; 9 import com.tc.management.beans.sessions.SessionMonitorMBean.SessionsComptroller; 10 import com.tc.object.bytecode.ManagerUtil; 11 import com.tc.object.bytecode.hook.impl.ClassProcessorHelper; 12 import com.terracotta.session.util.Assert; 13 import com.terracotta.session.util.ConfigProperties; 14 import com.terracotta.session.util.ContextMgr; 15 import com.terracotta.session.util.DefaultContextMgr; 16 import com.terracotta.session.util.LifecycleEventMgr; 17 import com.terracotta.session.util.Lock; 18 import com.terracotta.session.util.SessionCookieWriter; 19 import com.terracotta.session.util.SessionIdGenerator; 20 import com.terracotta.session.util.Timestamp; 21 22 import javax.servlet.http.HttpServletRequest ; 23 import javax.servlet.http.HttpServletResponse ; 24 25 public class TerracottaSessionManager { 26 27 private final SessionMonitorMBean mBean; 28 private final SessionIdGenerator idGenerator; 29 private final SessionCookieWriter cookieWriter; 30 private final SessionDataStore store; 31 private final LifecycleEventMgr eventMgr; 32 private final ContextMgr contextMgr; 33 private final boolean reqeustLogEnabled; 34 private final boolean invalidatorLogEnabled; 35 private final TCLogger logger; 36 private final RequestResponseFactory factory; 37 private final RequestTracker tracker; 38 private final boolean debugServerHops; 39 private final int debugServerHopsInterval; 40 private int serverHopsDetected = 0; 41 42 public TerracottaSessionManager(SessionIdGenerator sig, SessionCookieWriter scw, LifecycleEventMgr eventMgr, 43 ContextMgr contextMgr, RequestResponseFactory factory, ConfigProperties cp) { 44 45 Assert.pre(sig != null); 46 Assert.pre(scw != null); 47 Assert.pre(eventMgr != null); 48 Assert.pre(contextMgr != null); 49 50 this.idGenerator = sig; 51 this.cookieWriter = scw; 52 this.eventMgr = eventMgr; 53 this.contextMgr = contextMgr; 54 this.factory = factory; 55 this.store = new SessionDataStore(contextMgr.getAppName(), cp.getSessionTimeoutSeconds(), eventMgr, contextMgr); 56 this.logger = ManagerUtil.getLogger("com.tc.tcsession." + contextMgr.getAppName()); 57 this.reqeustLogEnabled = cp.getRequestLogBenchEnabled(); 58 this.invalidatorLogEnabled = cp.getInvalidatorLogBenchEnabled(); 59 60 Thread invalidator = new Thread (new SessionInvalidator(store, cp.getInvalidatorSleepSeconds(), cp 64 .getSessionTimeoutSeconds()), "SessionInvalidator - " + contextMgr.getAppName()); 65 invalidator.setDaemon(true); 66 invalidator.start(); 67 Assert.post(invalidator.isAlive()); 68 69 mBean = ManagerUtil.getSessionMonitorMBean(); 72 73 mBean.registerSessionsController(new SessionsComptroller() { 74 public boolean killSession(final String browserSessionId) { 75 SessionId id = idGenerator.makeInstanceFromBrowserId(browserSessionId); 76 if (id == null) { 77 id = idGenerator.makeInstanceFromInternalKey(browserSessionId); 79 } 80 expire(id); 81 return true; 82 } 83 }); 84 85 if (cp.isRequestTrackingEnabled()) { 86 tracker = new StuckRequestTracker(cp.getRequestTrackerSleepMillis(), cp.getRequestTrackerStuckThresholdMillis(), 87 cp.isDumpThreadsOnStuckRequests()); 88 ((StuckRequestTracker) tracker).start(); 89 } else { 90 tracker = new NullRequestTracker(); 91 } 92 93 this.debugServerHops = cp.isDebugServerHops(); 94 this.debugServerHopsInterval = cp.getDebugServerHopsInterval(); 95 } 96 97 public TerracottaRequest preprocess(HttpServletRequest req, HttpServletResponse res) { 98 tracker.begin(req); 99 TerracottaRequest terracottaRequest = basicPreprocess(req, res); 100 tracker.recordSessionId(terracottaRequest); 101 return terracottaRequest; 102 } 103 104 private TerracottaRequest basicPreprocess(HttpServletRequest req, HttpServletResponse res) { 105 Assert.pre(req != null); 106 Assert.pre(res != null); 107 108 SessionId sessionId = findSessionId(req); 109 110 if (debugServerHops) { 111 if (sessionId != null && sessionId.isServerHop()) { 112 synchronized (this) { 113 serverHopsDetected++; 114 if ((serverHopsDetected % debugServerHopsInterval) == 0) { 115 logger.info(serverHopsDetected + " server hops detected"); 116 } 117 } 118 } 119 } 120 121 TerracottaRequest rw = wrapRequest(sessionId, req, res); 122 123 Assert.post(rw != null); 124 return rw; 125 } 126 127 public TerracottaResponse createResponse(TerracottaRequest req, HttpServletResponse res) { 128 return factory.createResponse(req, res); 129 } 130 131 public void postprocess(TerracottaRequest req) { 132 try { 133 basicPostprocess(req); 134 } finally { 135 tracker.end(); 136 } 137 } 138 139 private void basicPostprocess(TerracottaRequest req) { 140 Assert.pre(req != null); 141 142 if (req.isForwarded()) return; 144 145 Assert.inv(!req.isForwarded()); 146 147 mBean.requestProcessed(); 148 149 try { 150 if (req.isSessionOwner()) postprocessSession(req); 151 } finally { 152 if (reqeustLogEnabled) { 153 logRequestBench(req); 154 } 155 } 156 } 157 158 private void logRequestBench(TerracottaRequest req) { 159 final String msgPrefix = "REQUEST BENCH: url=[" + req.getRequestURL() + "]"; 160 String sessionInfo = ""; 161 if (req.isSessionOwner()) { 162 final SessionId id = req.getTerracottaSession(false).getSessionId(); 163 sessionInfo = " sid=[" + id.getKey() + "] -> [" + id.getLock().getLockTimer().elapsed() + ":" 164 + id.getLock().getUnlockTimer().elapsed() + "]"; 165 } 166 final String msg = msgPrefix + sessionInfo + " -> " + (System.currentTimeMillis() - req.getRequestStartMillis()); 167 logger.info(msg); 168 } 169 170 private void postprocessSession(TerracottaRequest req) { 171 Assert.pre(req != null); 172 Assert.pre(!req.isForwarded()); 173 Assert.pre(req.isSessionOwner()); 174 final Session session = req.getTerracottaSession(false); 175 Assert.inv(session != null); 176 final SessionId id = session.getSessionId(); 177 final SessionData sd = session.getSessionData(); 178 try { 179 if (!session.isValid()) store.remove(id); 180 else { 181 sd.finishRequest(); 182 store.updateTimestampIfNeeded(sd); 183 } 184 } finally { 185 id.commitLock(); 186 } 187 } 188 189 194 public static void pauseRequest(final Session sess) { 195 Assert.pre(sess != null); 196 final SessionId id = sess.getSessionId(); 197 final SessionData sd = sess.getSessionData(); 198 sd.finishRequest(); 199 id.commitLock(); 200 } 201 202 205 public static void resumeRequest(final Session sess) { 206 Assert.pre(sess != null); 207 final SessionId id = sess.getSessionId(); 208 final SessionData sd = sess.getSessionData(); 209 id.getWriteLock(); 210 sd.startRequest(); 211 } 212 213 private TerracottaRequest wrapRequest(SessionId sessionId, HttpServletRequest req, HttpServletResponse res) { 214 TerracottaRequest request = factory.createRequest(sessionId, req, res); 215 request.setSessionManager(this); 216 return request; 217 } 218 219 223 protected Session getSession(final SessionId requestedSessionId, final HttpServletRequest req, 224 final HttpServletResponse res) { 225 Assert.pre(req != null); 226 Assert.pre(res != null); 227 Session rv = doGetSession(requestedSessionId, req, res); 228 Assert.post(rv != null); 229 return rv; 230 } 231 232 protected Session getSessionIfExists(SessionId requestedSessionId, HttpServletRequest req, HttpServletResponse res) { 233 if (requestedSessionId == null) return null; 234 SessionData sd = store.find(requestedSessionId); 235 if (sd == null) return null; 236 Assert.inv(sd.isValid()); 237 if (requestedSessionId.isServerHop()) cookieWriter.writeCookie(req, res, requestedSessionId); 238 return sd; 239 } 240 241 protected SessionCookieWriter getCookieWriter() { 242 return this.cookieWriter; 243 } 244 245 private void expire(SessionId id) { 246 SessionData sd = null; 247 try { 248 sd = store.find(id); 249 if (sd != null) { 250 expire(id, sd); 251 } 252 } finally { 253 if (sd != null) id.commitLock(); 254 } 255 } 256 257 private void expire(SessionId id, SessionData sd) { 258 try { 259 sd.invalidate(); 260 } catch (Throwable t) { 261 logger.error("unhandled exception during invalidate() for session " + id.getKey()); 262 } 263 store.remove(id); 264 mBean.sessionDestroyed(); 265 } 266 267 private Session doGetSession(final SessionId requestedSessionId, final HttpServletRequest req, 268 final HttpServletResponse res) { 269 Assert.pre(req != null); 270 Assert.pre(res != null); 271 272 if (requestedSessionId == null) { return createNewSession(req, res); } 273 final SessionData sd = store.find(requestedSessionId); 274 if (sd == null) { return createNewSession(req, res); } 275 Assert.inv(sd.isValid()); 276 if (requestedSessionId.isServerHop()) cookieWriter.writeCookie(req, res, requestedSessionId); 277 278 return sd; 279 } 280 281 private Session createNewSession(HttpServletRequest req, HttpServletResponse res) { 282 Assert.pre(req != null); 283 Assert.pre(res != null); 284 285 SessionId id = idGenerator.generateNewId(); 286 SessionData sd = store.createSessionData(id); 287 cookieWriter.writeCookie(req, res, id); 288 eventMgr.fireSessionCreatedEvent(sd); 289 mBean.sessionCreated(); 290 Assert.post(sd != null); 291 return sd; 292 } 293 294 private SessionId findSessionId(HttpServletRequest httpRequest) { 295 Assert.pre(httpRequest != null); 296 297 String requestedSessionId = httpRequest.getRequestedSessionId(); 298 if (requestedSessionId == null) return null; 299 else return idGenerator.makeInstanceFromBrowserId(requestedSessionId); 300 } 301 302 private class SessionInvalidator implements Runnable { 303 304 private final long sleepMillis; 305 306 public SessionInvalidator(final SessionDataStore store, final long sleepSeconds, 307 final long defaultSessionIdleSeconds) { 308 this.sleepMillis = sleepSeconds * 1000; 309 } 310 311 public void run() { 312 final String invalidatorLock = "tc:session_invalidator_lock_" + contextMgr.getAppName(); 313 314 while (true) { 315 sleep(sleepMillis); 316 if (Thread.interrupted()) { 317 break; 318 } else { 319 try { 320 final Lock lock = new Lock(invalidatorLock); 321 lock.tryWriteLock(); 322 if (!lock.isLocked()) continue; 323 try { 324 invalidateSessions(); 325 } finally { 326 lock.commitLock(); 327 } 328 } catch (Throwable t) { 329 logger.error("Unhandled exception occurred during session invalidation", t); 330 } 331 } 332 } 333 } 334 335 private void invalidateSessions() { 336 final long startMillis = System.currentTimeMillis(); 337 final String keys[] = store.getAllKeys(); 338 int totalCnt = 0; 339 int invalCnt = 0; 340 int evaled = 0; 341 int notEvaled = 0; 342 int errors = 0; 343 344 if (invalidatorLogEnabled) { 345 logger.info("SESSION INVALIDATOR: started"); 346 } 347 348 for (int i = 0, n = keys.length; i < n; i++) { 349 final String key = keys[i]; 350 try { 351 final SessionId id = idGenerator.makeInstanceFromInternalKey(key); 352 final Timestamp dtm = store.findTimestampUnlocked(id); 353 if (dtm == null) continue; 354 totalCnt++; 355 if (dtm.getMillis() < System.currentTimeMillis()) { 356 evaled++; 357 if (evaluateSession(dtm, id)) invalCnt++; 358 } else { 359 notEvaled++; 360 } 361 } catch (Throwable t) { 362 errors++; 363 logger.error("Unhandled exception inspecting session " + key + " for invalidation", t); 364 } 365 } 366 if (invalidatorLogEnabled) { 367 final String msg = "SESSION INVALIDATOR BENCH: " + " -> total=" + totalCnt + ", evaled=" + evaled 368 + ", notEvaled=" + notEvaled + ", errors=" + errors + ", invalidated=" + invalCnt 369 + " -> elapsed=" + (System.currentTimeMillis() - startMillis); 370 logger.info(msg); 371 } 372 } 373 374 private boolean evaluateSession(final Timestamp dtm, final SessionId id) { 375 Assert.pre(id != null); 376 377 boolean rv = false; 378 id.tryWriteLock(); 379 if (!id.getLock().isLocked()) { return rv; } 380 381 try { 382 final SessionData sd = store.findSessionDataUnlocked(id); 383 if (sd == null) return rv; 384 if (!sd.isValid()) { 385 expire(id, sd); 386 rv = true; 387 } else { 388 store.updateTimestampIfNeeded(sd); 389 } 390 } finally { 391 id.commitLock(); 392 } 393 return rv; 394 } 395 396 private void sleep(long l) { 397 try { 398 Thread.sleep(l); 399 } catch (InterruptedException ignore) { 400 } 402 } 403 } 404 405 public static boolean isDsoSessionApp(HttpServletRequest request) { 406 Assert.pre(request != null); 407 final String appName = DefaultContextMgr.computeAppName(request); 408 return ClassProcessorHelper.isDSOSessions(appName); 409 } 410 411 private static final class NullRequestTracker implements RequestTracker { 412 413 public final boolean end() { 414 return true; 415 } 416 417 public final void begin(HttpServletRequest req) { 418 } 420 421 public final void recordSessionId(TerracottaRequest terracottaRequest) { 422 } 424 425 } 426 427 } 428 | Popular Tags |