Mercurial Hosting > luan
comparison src/org/eclipse/jetty/server/session/HashSessionManager.java @ 802:3428c60d7cfc
replace jetty jars with source
author | Franklin Schmidt <fschmidt@gmail.com> |
---|---|
date | Wed, 07 Sep 2016 21:15:48 -0600 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
801:6a21393191c1 | 802:3428c60d7cfc |
---|---|
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.server.session; | |
20 | |
21 import java.io.DataInputStream; | |
22 import java.io.File; | |
23 import java.io.FileInputStream; | |
24 import java.io.IOException; | |
25 import java.io.InputStream; | |
26 import java.io.ObjectInputStream; | |
27 import java.net.URI; | |
28 import java.util.ArrayList; | |
29 import java.util.Iterator; | |
30 import java.util.Map; | |
31 import java.util.Set; | |
32 import java.util.Timer; | |
33 import java.util.TimerTask; | |
34 import java.util.concurrent.ConcurrentHashMap; | |
35 import java.util.concurrent.ConcurrentMap; | |
36 | |
37 import javax.servlet.ServletContext; | |
38 import javax.servlet.http.HttpServletRequest; | |
39 | |
40 import org.eclipse.jetty.server.handler.ContextHandler; | |
41 import org.eclipse.jetty.util.IO; | |
42 import org.eclipse.jetty.util.log.Logger; | |
43 | |
44 | |
45 /* ------------------------------------------------------------ */ | |
46 /** | |
47 * HashSessionManager | |
48 * | |
49 * An in-memory implementation of SessionManager. | |
50 * <p> | |
51 * This manager supports saving sessions to disk, either periodically or at shutdown. | |
52 * Sessions can also have their content idle saved to disk to reduce the memory overheads of large idle sessions. | |
53 * <p> | |
54 * This manager will create it's own Timer instance to scavenge threads, unless it discovers a shared Timer instance | |
55 * set as the "org.eclipse.jetty.server.session.timer" attribute of the ContextHandler. | |
56 * | |
57 */ | |
58 public class HashSessionManager extends AbstractSessionManager | |
59 { | |
60 final static Logger __log = SessionHandler.LOG; | |
61 | |
62 protected final ConcurrentMap<String,HashedSession> _sessions=new ConcurrentHashMap<String,HashedSession>(); | |
63 private static int __id; | |
64 private Timer _timer; | |
65 private boolean _timerStop=false; | |
66 private TimerTask _task; | |
67 long _scavengePeriodMs=30000; | |
68 long _savePeriodMs=0; //don't do period saves by default | |
69 long _idleSavePeriodMs = 0; // don't idle save sessions by default. | |
70 private TimerTask _saveTask; | |
71 File _storeDir; | |
72 private boolean _lazyLoad=false; | |
73 private volatile boolean _sessionsLoaded=false; | |
74 private boolean _deleteUnrestorableSessions=false; | |
75 | |
76 | |
77 | |
78 | |
79 /* ------------------------------------------------------------ */ | |
80 public HashSessionManager() | |
81 { | |
82 super(); | |
83 } | |
84 | |
85 /* ------------------------------------------------------------ */ | |
86 /** | |
87 * @see org.eclipse.jetty.servlet.AbstractSessionManager#doStart() | |
88 */ | |
89 @Override | |
90 public void doStart() throws Exception | |
91 { | |
92 super.doStart(); | |
93 | |
94 _timerStop=false; | |
95 ServletContext context = ContextHandler.getCurrentContext(); | |
96 if (context!=null) | |
97 _timer=(Timer)context.getAttribute("org.eclipse.jetty.server.session.timer"); | |
98 if (_timer==null) | |
99 { | |
100 _timerStop=true; | |
101 _timer=new Timer("HashSessionScavenger-"+__id++, true); | |
102 } | |
103 | |
104 setScavengePeriod(getScavengePeriod()); | |
105 | |
106 if (_storeDir!=null) | |
107 { | |
108 if (!_storeDir.exists()) | |
109 _storeDir.mkdirs(); | |
110 | |
111 if (!_lazyLoad) | |
112 restoreSessions(); | |
113 } | |
114 | |
115 setSavePeriod(getSavePeriod()); | |
116 } | |
117 | |
118 /* ------------------------------------------------------------ */ | |
119 /** | |
120 * @see org.eclipse.jetty.servlet.AbstractSessionManager#doStop() | |
121 */ | |
122 @Override | |
123 public void doStop() throws Exception | |
124 { | |
125 // stop the scavengers | |
126 synchronized(this) | |
127 { | |
128 if (_saveTask!=null) | |
129 _saveTask.cancel(); | |
130 _saveTask=null; | |
131 if (_task!=null) | |
132 _task.cancel(); | |
133 _task=null; | |
134 if (_timer!=null && _timerStop) | |
135 _timer.cancel(); | |
136 _timer=null; | |
137 } | |
138 | |
139 // This will callback invalidate sessions - where we decide if we will save | |
140 super.doStop(); | |
141 | |
142 _sessions.clear(); | |
143 | |
144 } | |
145 | |
146 /* ------------------------------------------------------------ */ | |
147 /** | |
148 * @return the period in seconds at which a check is made for sessions to be invalidated. | |
149 */ | |
150 public int getScavengePeriod() | |
151 { | |
152 return (int)(_scavengePeriodMs/1000); | |
153 } | |
154 | |
155 | |
156 /* ------------------------------------------------------------ */ | |
157 @Override | |
158 public int getSessions() | |
159 { | |
160 int sessions=super.getSessions(); | |
161 if (__log.isDebugEnabled()) | |
162 { | |
163 if (_sessions.size()!=sessions) | |
164 __log.warn("sessions: "+_sessions.size()+"!="+sessions); | |
165 } | |
166 return sessions; | |
167 } | |
168 | |
169 /* ------------------------------------------------------------ */ | |
170 /** | |
171 * @return seconds Idle period after which a session is saved | |
172 */ | |
173 public int getIdleSavePeriod() | |
174 { | |
175 if (_idleSavePeriodMs <= 0) | |
176 return 0; | |
177 | |
178 return (int)(_idleSavePeriodMs / 1000); | |
179 } | |
180 | |
181 /* ------------------------------------------------------------ */ | |
182 /** | |
183 * Configures the period in seconds after which a session is deemed idle and saved | |
184 * to save on session memory. | |
185 * | |
186 * The session is persisted, the values attribute map is cleared and the session set to idled. | |
187 * | |
188 * @param seconds Idle period after which a session is saved | |
189 */ | |
190 public void setIdleSavePeriod(int seconds) | |
191 { | |
192 _idleSavePeriodMs = seconds * 1000L; | |
193 } | |
194 | |
195 /* ------------------------------------------------------------ */ | |
196 @Override | |
197 public void setMaxInactiveInterval(int seconds) | |
198 { | |
199 super.setMaxInactiveInterval(seconds); | |
200 if (_dftMaxIdleSecs>0&&_scavengePeriodMs>_dftMaxIdleSecs*1000L) | |
201 setScavengePeriod((_dftMaxIdleSecs+9)/10); | |
202 } | |
203 | |
204 /* ------------------------------------------------------------ */ | |
205 /** | |
206 * @param seconds the period is seconds at which sessions are periodically saved to disk | |
207 */ | |
208 public void setSavePeriod (int seconds) | |
209 { | |
210 long period = (seconds * 1000L); | |
211 if (period < 0) | |
212 period=0; | |
213 _savePeriodMs=period; | |
214 | |
215 if (_timer!=null) | |
216 { | |
217 synchronized (this) | |
218 { | |
219 if (_saveTask!=null) | |
220 _saveTask.cancel(); | |
221 if (_savePeriodMs > 0 && _storeDir!=null) //only save if we have a directory configured | |
222 { | |
223 _saveTask = new TimerTask() | |
224 { | |
225 @Override | |
226 public void run() | |
227 { | |
228 try | |
229 { | |
230 saveSessions(true); | |
231 } | |
232 catch (Exception e) | |
233 { | |
234 __log.warn(e); | |
235 } | |
236 } | |
237 }; | |
238 _timer.schedule(_saveTask,_savePeriodMs,_savePeriodMs); | |
239 } | |
240 } | |
241 } | |
242 } | |
243 | |
244 /* ------------------------------------------------------------ */ | |
245 /** | |
246 * @return the period in seconds at which sessions are periodically saved to disk | |
247 */ | |
248 public int getSavePeriod () | |
249 { | |
250 if (_savePeriodMs<=0) | |
251 return 0; | |
252 | |
253 return (int)(_savePeriodMs/1000); | |
254 } | |
255 | |
256 /* ------------------------------------------------------------ */ | |
257 /** | |
258 * @param seconds the period in seconds at which a check is made for sessions to be invalidated. | |
259 */ | |
260 public void setScavengePeriod(int seconds) | |
261 { | |
262 if (seconds==0) | |
263 seconds=60; | |
264 | |
265 long old_period=_scavengePeriodMs; | |
266 long period=seconds*1000L; | |
267 if (period>60000) | |
268 period=60000; | |
269 if (period<1000) | |
270 period=1000; | |
271 | |
272 _scavengePeriodMs=period; | |
273 | |
274 if (_timer!=null && (period!=old_period || _task==null)) | |
275 { | |
276 synchronized (this) | |
277 { | |
278 if (_task!=null) | |
279 _task.cancel(); | |
280 _task = new TimerTask() | |
281 { | |
282 @Override | |
283 public void run() | |
284 { | |
285 scavenge(); | |
286 } | |
287 }; | |
288 _timer.schedule(_task,_scavengePeriodMs,_scavengePeriodMs); | |
289 } | |
290 } | |
291 } | |
292 | |
293 /* -------------------------------------------------------------- */ | |
294 /** | |
295 * Find sessions that have timed out and invalidate them. This runs in the | |
296 * SessionScavenger thread. | |
297 */ | |
298 protected void scavenge() | |
299 { | |
300 //don't attempt to scavenge if we are shutting down | |
301 if (isStopping() || isStopped()) | |
302 return; | |
303 | |
304 Thread thread=Thread.currentThread(); | |
305 ClassLoader old_loader=thread.getContextClassLoader(); | |
306 try | |
307 { | |
308 if (_loader!=null) | |
309 thread.setContextClassLoader(_loader); | |
310 | |
311 // For each session | |
312 long now=System.currentTimeMillis(); | |
313 | |
314 for (Iterator<HashedSession> i=_sessions.values().iterator(); i.hasNext();) | |
315 { | |
316 HashedSession session=i.next(); | |
317 long idleTime=session.getMaxInactiveInterval()*1000L; | |
318 if (idleTime>0&&session.getAccessed()+idleTime<now) | |
319 { | |
320 // Found a stale session, add it to the list | |
321 try | |
322 { | |
323 session.timeout(); | |
324 } | |
325 catch (Exception e) | |
326 { | |
327 __log.warn("Problem scavenging sessions", e); | |
328 } | |
329 } | |
330 else if (_idleSavePeriodMs > 0 && session.getAccessed()+_idleSavePeriodMs < now) | |
331 { | |
332 try | |
333 { | |
334 session.idle(); | |
335 } | |
336 catch (Exception e) | |
337 { | |
338 __log.warn("Problem idling session "+ session.getId(), e); | |
339 } | |
340 } | |
341 } | |
342 } | |
343 finally | |
344 { | |
345 thread.setContextClassLoader(old_loader); | |
346 } | |
347 } | |
348 | |
349 /* ------------------------------------------------------------ */ | |
350 @Override | |
351 protected void addSession(AbstractSession session) | |
352 { | |
353 if (isRunning()) | |
354 _sessions.put(session.getClusterId(),(HashedSession)session); | |
355 } | |
356 | |
357 /* ------------------------------------------------------------ */ | |
358 @Override | |
359 public AbstractSession getSession(String idInCluster) | |
360 { | |
361 if ( _lazyLoad && !_sessionsLoaded) | |
362 { | |
363 try | |
364 { | |
365 restoreSessions(); | |
366 } | |
367 catch(Exception e) | |
368 { | |
369 __log.warn(e); | |
370 } | |
371 } | |
372 | |
373 Map<String,HashedSession> sessions=_sessions; | |
374 if (sessions==null) | |
375 return null; | |
376 | |
377 HashedSession session = sessions.get(idInCluster); | |
378 | |
379 if (session == null && _lazyLoad) | |
380 session=restoreSession(idInCluster); | |
381 if (session == null) | |
382 return null; | |
383 | |
384 if (_idleSavePeriodMs!=0) | |
385 session.deIdle(); | |
386 | |
387 return session; | |
388 } | |
389 | |
390 /* ------------------------------------------------------------ */ | |
391 @Override | |
392 protected void invalidateSessions() throws Exception | |
393 { | |
394 // Invalidate all sessions to cause unbind events | |
395 ArrayList<HashedSession> sessions=new ArrayList<HashedSession>(_sessions.values()); | |
396 int loop=100; | |
397 while (sessions.size()>0 && loop-->0) | |
398 { | |
399 // If we are called from doStop | |
400 if (isStopping() && _storeDir != null && _storeDir.exists() && _storeDir.canWrite()) | |
401 { | |
402 // Then we only save and remove the session - it is not invalidated. | |
403 for (HashedSession session : sessions) | |
404 { | |
405 session.save(false); | |
406 removeSession(session,false); | |
407 } | |
408 } | |
409 else | |
410 { | |
411 for (HashedSession session : sessions) | |
412 session.invalidate(); | |
413 } | |
414 | |
415 // check that no new sessions were created while we were iterating | |
416 sessions=new ArrayList<HashedSession>(_sessions.values()); | |
417 } | |
418 } | |
419 | |
420 /* ------------------------------------------------------------ */ | |
421 @Override | |
422 protected AbstractSession newSession(HttpServletRequest request) | |
423 { | |
424 return new HashedSession(this, request); | |
425 } | |
426 | |
427 /* ------------------------------------------------------------ */ | |
428 protected AbstractSession newSession(long created, long accessed, String clusterId) | |
429 { | |
430 return new HashedSession(this, created,accessed, clusterId); | |
431 } | |
432 | |
433 /* ------------------------------------------------------------ */ | |
434 @Override | |
435 protected boolean removeSession(String clusterId) | |
436 { | |
437 return _sessions.remove(clusterId)!=null; | |
438 } | |
439 | |
440 /* ------------------------------------------------------------ */ | |
441 public void setStoreDirectory (File dir) throws IOException | |
442 { | |
443 // CanonicalFile is used to capture the base store directory in a way that will | |
444 // work on Windows. Case differences may through off later checks using this directory. | |
445 _storeDir=dir.getCanonicalFile(); | |
446 } | |
447 | |
448 /* ------------------------------------------------------------ */ | |
449 public File getStoreDirectory () | |
450 { | |
451 return _storeDir; | |
452 } | |
453 | |
454 /* ------------------------------------------------------------ */ | |
455 public void setLazyLoad(boolean lazyLoad) | |
456 { | |
457 _lazyLoad = lazyLoad; | |
458 } | |
459 | |
460 /* ------------------------------------------------------------ */ | |
461 public boolean isLazyLoad() | |
462 { | |
463 return _lazyLoad; | |
464 } | |
465 | |
466 /* ------------------------------------------------------------ */ | |
467 public boolean isDeleteUnrestorableSessions() | |
468 { | |
469 return _deleteUnrestorableSessions; | |
470 } | |
471 | |
472 /* ------------------------------------------------------------ */ | |
473 public void setDeleteUnrestorableSessions(boolean deleteUnrestorableSessions) | |
474 { | |
475 _deleteUnrestorableSessions = deleteUnrestorableSessions; | |
476 } | |
477 | |
478 /* ------------------------------------------------------------ */ | |
479 public void restoreSessions () throws Exception | |
480 { | |
481 _sessionsLoaded = true; | |
482 | |
483 if (_storeDir==null || !_storeDir.exists()) | |
484 { | |
485 return; | |
486 } | |
487 | |
488 if (!_storeDir.canRead()) | |
489 { | |
490 __log.warn ("Unable to restore Sessions: Cannot read from Session storage directory "+_storeDir.getAbsolutePath()); | |
491 return; | |
492 } | |
493 | |
494 String[] files = _storeDir.list(); | |
495 for (int i=0;files!=null&&i<files.length;i++) | |
496 { | |
497 restoreSession(files[i]); | |
498 } | |
499 } | |
500 | |
501 /* ------------------------------------------------------------ */ | |
502 protected synchronized HashedSession restoreSession(String idInCuster) | |
503 { | |
504 File file = new File(_storeDir,idInCuster); | |
505 | |
506 FileInputStream in = null; | |
507 Exception error = null; | |
508 try | |
509 { | |
510 if (file.exists()) | |
511 { | |
512 in = new FileInputStream(file); | |
513 HashedSession session = restoreSession(in, null); | |
514 addSession(session, false); | |
515 session.didActivate(); | |
516 return session; | |
517 } | |
518 } | |
519 catch (Exception e) | |
520 { | |
521 error = e; | |
522 } | |
523 finally | |
524 { | |
525 if (in != null) IO.close(in); | |
526 | |
527 if (error != null) | |
528 { | |
529 if (isDeleteUnrestorableSessions() && file.exists() && file.getParentFile().equals(_storeDir) ) | |
530 { | |
531 file.delete(); | |
532 __log.warn("Deleting file for unrestorable session "+idInCuster, error); | |
533 } | |
534 else | |
535 { | |
536 __log.warn("Problem restoring session "+idInCuster, error); | |
537 } | |
538 } | |
539 else | |
540 file.delete(); //delete successfully restored file | |
541 | |
542 } | |
543 return null; | |
544 } | |
545 | |
546 /* ------------------------------------------------------------ */ | |
547 public void saveSessions(boolean reactivate) throws Exception | |
548 { | |
549 if (_storeDir==null || !_storeDir.exists()) | |
550 { | |
551 return; | |
552 } | |
553 | |
554 if (!_storeDir.canWrite()) | |
555 { | |
556 __log.warn ("Unable to save Sessions: Session persistence storage directory "+_storeDir.getAbsolutePath()+ " is not writeable"); | |
557 return; | |
558 } | |
559 | |
560 for (HashedSession session : _sessions.values()) | |
561 session.save(true); | |
562 } | |
563 | |
564 /* ------------------------------------------------------------ */ | |
565 public HashedSession restoreSession (InputStream is, HashedSession session) throws Exception | |
566 { | |
567 /* | |
568 * Take care of this class's fields first by calling | |
569 * defaultReadObject | |
570 */ | |
571 DataInputStream in = new DataInputStream(is); | |
572 try | |
573 { | |
574 String clusterId = in.readUTF(); | |
575 in.readUTF(); // nodeId | |
576 long created = in.readLong(); | |
577 long accessed = in.readLong(); | |
578 int requests = in.readInt(); | |
579 | |
580 if (session == null) | |
581 session = (HashedSession)newSession(created, accessed, clusterId); | |
582 session.setRequests(requests); | |
583 int size = in.readInt(); | |
584 if (size>0) | |
585 { | |
586 ClassLoadingObjectInputStream ois = new ClassLoadingObjectInputStream(in); | |
587 try | |
588 { | |
589 for (int i=0; i<size;i++) | |
590 { | |
591 String key = ois.readUTF(); | |
592 Object value = ois.readObject(); | |
593 session.setAttribute(key,value); | |
594 } | |
595 } | |
596 finally | |
597 { | |
598 IO.close(ois); | |
599 } | |
600 } | |
601 return session; | |
602 } | |
603 finally | |
604 { | |
605 IO.close(in); | |
606 } | |
607 } | |
608 | |
609 | |
610 /* ------------------------------------------------------------ */ | |
611 /* ------------------------------------------------------------ */ | |
612 protected class ClassLoadingObjectInputStream extends ObjectInputStream | |
613 { | |
614 /* ------------------------------------------------------------ */ | |
615 public ClassLoadingObjectInputStream(java.io.InputStream in) throws IOException | |
616 { | |
617 super(in); | |
618 } | |
619 | |
620 /* ------------------------------------------------------------ */ | |
621 public ClassLoadingObjectInputStream () throws IOException | |
622 { | |
623 super(); | |
624 } | |
625 | |
626 /* ------------------------------------------------------------ */ | |
627 @Override | |
628 public Class<?> resolveClass (java.io.ObjectStreamClass cl) throws IOException, ClassNotFoundException | |
629 { | |
630 try | |
631 { | |
632 return Class.forName(cl.getName(), false, Thread.currentThread().getContextClassLoader()); | |
633 } | |
634 catch (ClassNotFoundException e) | |
635 { | |
636 return super.resolveClass(cl); | |
637 } | |
638 } | |
639 } | |
640 } |