1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package org.eclipse.jetty.server.session;
16
17 import java.io.ByteArrayInputStream;
18 import java.io.ByteArrayOutputStream;
19 import java.io.IOException;
20 import java.io.InputStream;
21 import java.io.ObjectInputStream;
22 import java.io.ObjectOutputStream;
23 import java.sql.Connection;
24 import java.sql.PreparedStatement;
25 import java.sql.ResultSet;
26 import java.sql.SQLException;
27 import java.util.Collections;
28 import java.util.List;
29 import java.util.ListIterator;
30 import java.util.Map;
31 import java.util.concurrent.ConcurrentHashMap;
32
33 import javax.servlet.http.HttpServletRequest;
34 import javax.servlet.http.HttpSessionEvent;
35 import javax.servlet.http.HttpSessionListener;
36
37 import org.eclipse.jetty.server.SessionIdManager;
38 import org.eclipse.jetty.server.handler.ContextHandler;
39 import org.eclipse.jetty.util.LazyList;
40 import org.eclipse.jetty.util.log.Log;
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66 public class JDBCSessionManager extends AbstractSessionManager
67 {
68 protected String __insertSession;
69 protected String __deleteSession;
70 protected String __selectSession;
71 protected String __updateSession;
72 protected String __updateSessionNode;
73 protected String __updateSessionAccessTime;
74
75 private ConcurrentHashMap _sessions;
76 protected long _saveIntervalSec = 60;
77
78
79
80
81
82
83 public class SessionData
84 {
85 private final String _id;
86 private String _rowId;
87 private long _accessed;
88 private long _lastAccessed;
89 private long _maxIdleMs;
90 private long _cookieSet;
91 private long _created;
92 private Map _attributes;
93 private String _lastNode;
94 private String _canonicalContext;
95 private long _lastSaved;
96 private long _expiryTime;
97 private String _virtualHost;
98
99 public SessionData (String sessionId)
100 {
101 _id=sessionId;
102 _created=System.currentTimeMillis();
103 _accessed = _created;
104 _attributes = new ConcurrentHashMap();
105 _lastNode = getIdManager().getWorkerName();
106 }
107
108 public synchronized String getId ()
109 {
110 return _id;
111 }
112
113 public synchronized long getCreated ()
114 {
115 return _created;
116 }
117
118 protected synchronized void setCreated (long ms)
119 {
120 _created = ms;
121 }
122
123 public synchronized long getAccessed ()
124 {
125 return _accessed;
126 }
127
128 protected synchronized void setAccessed (long ms)
129 {
130 _accessed = ms;
131 }
132
133
134 public synchronized void setMaxIdleMs (long ms)
135 {
136 _maxIdleMs = ms;
137 }
138
139 public synchronized long getMaxIdleMs()
140 {
141 return _maxIdleMs;
142 }
143
144 public synchronized void setLastAccessed (long ms)
145 {
146 _lastAccessed = ms;
147 }
148
149 public synchronized long getLastAccessed()
150 {
151 return _lastAccessed;
152 }
153
154 public void setCookieSet (long ms)
155 {
156 _cookieSet = ms;
157 }
158
159 public synchronized long getCookieSet ()
160 {
161 return _cookieSet;
162 }
163
164 public synchronized void setRowId (String rowId)
165 {
166 _rowId=rowId;
167 }
168
169 protected synchronized String getRowId()
170 {
171 return _rowId;
172 }
173
174 protected synchronized Map getAttributeMap ()
175 {
176 return _attributes;
177 }
178
179 protected synchronized void setAttributeMap (ConcurrentHashMap map)
180 {
181 _attributes = map;
182 }
183
184 public synchronized void setLastNode (String node)
185 {
186 _lastNode=node;
187 }
188
189 public synchronized String getLastNode ()
190 {
191 return _lastNode;
192 }
193
194 public synchronized void setCanonicalContext(String str)
195 {
196 _canonicalContext=str;
197 }
198
199 public synchronized String getCanonicalContext ()
200 {
201 return _canonicalContext;
202 }
203
204 public synchronized long getLastSaved ()
205 {
206 return _lastSaved;
207 }
208
209 public synchronized void setLastSaved (long time)
210 {
211 _lastSaved=time;
212 }
213
214 public synchronized void setExpiryTime (long time)
215 {
216 _expiryTime=time;
217 }
218
219 public synchronized long getExpiryTime ()
220 {
221 return _expiryTime;
222 }
223
224 public synchronized void setVirtualHost (String vhost)
225 {
226 _virtualHost=vhost;
227 }
228
229 public synchronized String getVirtualHost ()
230 {
231 return _virtualHost;
232 }
233
234 public String toString ()
235 {
236 return "Session rowId="+_rowId+",id="+_id+",lastNode="+_lastNode+
237 ",created="+_created+",accessed="+_accessed+
238 ",lastAccessed="+_lastAccessed+",cookieSet="+_cookieSet+
239 "lastSaved="+_lastSaved;
240 }
241 }
242
243
244
245
246
247
248
249
250 public class Session extends AbstractSessionManager.Session
251 {
252 private final SessionData _data;
253 private boolean _dirty=false;
254
255
256
257
258
259
260 protected Session (HttpServletRequest request)
261 {
262
263 super(request);
264 _data = new SessionData(_clusterId);
265 _data.setMaxIdleMs(_dftMaxIdleSecs*1000);
266 _data.setCanonicalContext(canonicalize(_context.getContextPath()));
267 _data.setVirtualHost(getVirtualHost(_context));
268 _data.setExpiryTime(_maxIdleMs < 0 ? 0 : (System.currentTimeMillis() + _maxIdleMs));
269 _values=_data.getAttributeMap();
270 }
271
272
273
274
275
276 protected Session (SessionData data)
277 {
278 super(data.getCreated(), data.getId());
279 _data=data;
280 _values=data.getAttributeMap();
281 }
282
283 protected Map newAttributeMap()
284 {
285 return _data.getAttributeMap();
286 }
287
288 public void setAttribute (String name, Object value)
289 {
290 super.setAttribute(name, value);
291 _dirty=true;
292 }
293
294 public void removeAttribute (String name)
295 {
296 super.removeAttribute(name);
297 _dirty=true;
298 }
299
300 protected void cookieSet()
301 {
302 _data.setCookieSet(_data.getAccessed());
303 }
304
305
306
307
308
309
310
311 protected void access(long time)
312 {
313 super.access(time);
314 _data.setLastAccessed(_data.getAccessed());
315 _data.setAccessed(time);
316 _data.setExpiryTime(_maxIdleMs < 0 ? 0 : (time + _maxIdleMs));
317 }
318
319
320
321
322
323 protected void complete()
324 {
325 super.complete();
326 try
327 {
328 if (_dirty)
329 {
330
331
332 willPassivate();
333 updateSession(_data);
334 didActivate();
335 }
336 else if ((_data._accessed - _data._lastSaved) >= (getSaveInterval() * 1000))
337 updateSessionAccessTime(_data);
338
339 }
340 catch (Exception e)
341 {
342 Log.warn("Problem persisting changed session data id="+getId(), e);
343 }
344 finally
345 {
346 _dirty=false;
347 }
348 }
349
350 protected void timeout() throws IllegalStateException
351 {
352 if (Log.isDebugEnabled()) Log.debug("Timing out session id="+getClusterId());
353 super.timeout();
354 }
355 }
356
357
358
359
360
361
362
363
364
365 protected class ClassLoadingObjectInputStream extends ObjectInputStream
366 {
367 public ClassLoadingObjectInputStream(java.io.InputStream in) throws IOException
368 {
369 super(in);
370 }
371
372 public ClassLoadingObjectInputStream () throws IOException
373 {
374 super();
375 }
376
377 public Class resolveClass (java.io.ObjectStreamClass cl) throws IOException, ClassNotFoundException
378 {
379 try
380 {
381 return Class.forName(cl.getName(), false, Thread.currentThread().getContextClassLoader());
382 }
383 catch (ClassNotFoundException e)
384 {
385 return super.resolveClass(cl);
386 }
387 }
388 }
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411 public void setSaveInterval (long sec)
412 {
413 _saveIntervalSec=sec;
414 }
415
416 public long getSaveInterval ()
417 {
418 return _saveIntervalSec;
419 }
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438 public Session getSession(String idInCluster)
439 {
440 Session session = (Session)_sessions.get(idInCluster);
441
442 synchronized (this)
443 {
444 try
445 {
446
447
448
449
450
451
452
453 SessionData data = null;
454 long now = System.currentTimeMillis();
455 if (Log.isDebugEnabled()) Log.debug("now="+now+
456 " lastSaved="+(session==null?0:session._data._lastSaved)+
457 " interval="+(_saveIntervalSec * 1000)+
458 " difference="+(now - (session==null?0:session._data._lastSaved)));
459 if (session==null || ((now - session._data._lastSaved) >= (_saveIntervalSec * 1000)))
460 {
461 data = loadSession(idInCluster, canonicalize(_context.getContextPath()), getVirtualHost(_context));
462 }
463 else
464 data = session._data;
465
466 if (data != null)
467 {
468 if (!data.getLastNode().equals(getIdManager().getWorkerName()) || session==null)
469 {
470
471 session = new Session(data);
472 _sessions.put(idInCluster, session);
473 session.didActivate();
474
475
476 updateSessionNode(data);
477 }
478 else
479 if (Log.isDebugEnabled()) Log.debug("Session not stale "+session._data);
480
481 }
482 else
483 {
484
485 session=null;
486 if (Log.isDebugEnabled()) Log.debug("No session in database matching id="+idInCluster);
487 }
488
489 return session;
490 }
491 catch (Exception e)
492 {
493 Log.warn("Unable to load session from database", e);
494 return null;
495 }
496 }
497 }
498
499
500
501
502
503
504
505 public Map getSessionMap()
506 {
507 return Collections.unmodifiableMap(_sessions);
508 }
509
510
511
512
513
514
515
516 public int getSessions()
517 {
518 int size = 0;
519 synchronized (this)
520 {
521 size = _sessions.size();
522 }
523 return size;
524 }
525
526
527
528
529
530
531
532 public void doStart() throws Exception
533 {
534 if (_sessionIdManager==null)
535 throw new IllegalStateException("No session id manager defined");
536
537 prepareTables();
538
539 _sessions = new ConcurrentHashMap();
540 super.doStart();
541 }
542
543
544
545
546
547
548
549 public void doStop() throws Exception
550 {
551 _sessions.clear();
552 _sessions = null;
553
554 super.doStop();
555 }
556
557 protected void invalidateSessions()
558 {
559
560
561
562
563
564
565 }
566
567
568
569
570
571
572
573 protected void invalidateSession (String idInCluster)
574 {
575 synchronized (this)
576 {
577 Session session = (Session)_sessions.get(idInCluster);
578 if (session != null)
579 {
580 session.invalidate();
581 }
582 }
583 }
584
585
586
587
588
589
590
591 protected void removeSession(String idInCluster)
592 {
593 synchronized (this)
594 {
595 try
596 {
597 Session session = (Session)_sessions.remove(idInCluster);
598 deleteSession(session._data);
599 }
600 catch (Exception e)
601 {
602 Log.warn("Problem deleting session id="+idInCluster, e);
603 }
604 }
605 }
606
607
608
609
610
611
612
613 protected void addSession(AbstractSessionManager.Session session)
614 {
615 if (session==null)
616 return;
617
618 synchronized (this)
619 {
620 _sessions.put(session.getClusterId(), session);
621
622
623 try
624 {
625 session.willPassivate();
626 storeSession(((JDBCSessionManager.Session)session)._data);
627 session.didActivate();
628 }
629 catch (Exception e)
630 {
631 Log.warn("Unable to store new session id="+session.getId() , e);
632 }
633 }
634 }
635
636
637
638
639
640
641
642 protected AbstractSessionManager.Session newSession(HttpServletRequest request)
643 {
644 return new Session(request);
645 }
646
647
648
649
650
651
652
653 public void removeSession(AbstractSessionManager.Session session, boolean invalidate)
654 {
655
656 synchronized (_sessionIdManager)
657 {
658 boolean removed = false;
659
660 synchronized (this)
661 {
662
663 if (_sessions.get(session.getClusterId()) != null)
664 {
665 removed = true;
666 removeSession(session.getClusterId());
667 }
668 }
669
670 if (removed)
671 {
672
673 _sessionIdManager.removeSession(session);
674 if (invalidate)
675 _sessionIdManager.invalidateAll(session.getClusterId());
676 }
677 }
678
679 if (invalidate && _sessionListeners!=null)
680 {
681 HttpSessionEvent event=new HttpSessionEvent(session);
682 for (int i=LazyList.size(_sessionListeners); i-->0;)
683 ((HttpSessionListener)LazyList.get(_sessionListeners,i)).sessionDestroyed(event);
684 }
685 if (!invalidate)
686 {
687 session.willPassivate();
688 }
689 }
690
691
692
693
694
695
696
697
698 protected void expire (List sessionIds)
699 {
700
701 if (isStopping() || isStopped())
702 return;
703
704
705 Thread thread=Thread.currentThread();
706 ClassLoader old_loader=thread.getContextClassLoader();
707 ListIterator itor = sessionIds.listIterator();
708
709 try
710 {
711 while (itor.hasNext())
712 {
713 String sessionId = (String)itor.next();
714 if (Log.isDebugEnabled()) Log.debug("Expiring session id "+sessionId);
715 Session session = (Session)_sessions.get(sessionId);
716 if (session != null)
717 {
718 session.timeout();
719 itor.remove();
720 int count = this._sessions.size();
721 if (count < this._minSessions)
722 this._minSessions=count;
723 }
724 else
725 {
726 if (Log.isDebugEnabled()) Log.debug("Unrecognized session id="+sessionId);
727 }
728 }
729 }
730 catch (Throwable t)
731 {
732 if (t instanceof ThreadDeath)
733 throw ((ThreadDeath)t);
734 else
735 Log.warn("Problem expiring sessions", t);
736 }
737 finally
738 {
739 thread.setContextClassLoader(old_loader);
740 }
741 }
742
743
744 protected void prepareTables ()
745 {
746 __insertSession = "insert into "+((JDBCSessionIdManager)_sessionIdManager)._sessionTable+
747 " (rowId, sessionId, contextPath, virtualHost, lastNode, accessTime, lastAccessTime, createTime, cookieTime, lastSavedTime, expiryTime, map) "+
748 " values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
749
750 __deleteSession = "delete from "+((JDBCSessionIdManager)_sessionIdManager)._sessionTable+
751 " where rowId = ?";
752
753 __selectSession = "select * from "+((JDBCSessionIdManager)_sessionIdManager)._sessionTable+
754 " where sessionId = ? and contextPath = ? and virtualHost = ?";
755
756 __updateSession = "update "+((JDBCSessionIdManager)_sessionIdManager)._sessionTable+
757 " set lastNode = ?, accessTime = ?, lastAccessTime = ?, lastSavedTime = ?, expiryTime = ?, map = ? where rowId = ?";
758
759 __updateSessionNode = "update "+((JDBCSessionIdManager)_sessionIdManager)._sessionTable+
760 " set lastNode = ? where rowId = ?";
761
762 __updateSessionAccessTime = "update "+((JDBCSessionIdManager)_sessionIdManager)._sessionTable+
763 " set lastNode = ?, accessTime = ?, lastAccessTime = ?, lastSavedTime = ?, expiryTime = ? where rowId = ?";
764 }
765
766
767
768
769
770
771
772 protected SessionData loadSession (String id, String canonicalContextPath, String vhost)
773 throws Exception
774 {
775 SessionData data = null;
776 Connection connection = getConnection();
777 PreparedStatement statement = null;
778 try
779 {
780 statement = connection.prepareStatement(__selectSession);
781 statement.setString(1, id);
782 statement.setString(2, canonicalContextPath);
783 statement.setString(3, vhost);
784 ResultSet result = statement.executeQuery();
785 if (result.next())
786 {
787 data = new SessionData(id);
788 data.setRowId(result.getString("rowId"));
789 data.setCookieSet(result.getLong("cookieTime"));
790 data.setLastAccessed(result.getLong("lastAccessTime"));
791 data.setAccessed (result.getLong("accessTime"));
792 data.setCreated(result.getLong("createTime"));
793 data.setLastNode(result.getString("lastNode"));
794 data.setLastSaved(result.getLong("lastSavedTime"));
795 data.setExpiryTime(result.getLong("expiryTime"));
796 data.setCanonicalContext(result.getString("contextPath"));
797 data.setVirtualHost(result.getString("virtualHost"));
798
799 InputStream is = ((JDBCSessionIdManager)getIdManager())._dbAdaptor.getBlobInputStream(result, "map");
800 ClassLoadingObjectInputStream ois = new ClassLoadingObjectInputStream (is);
801 Object o = ois.readObject();
802 data.setAttributeMap((ConcurrentHashMap)o);
803 ois.close();
804
805 if (Log.isDebugEnabled())
806 Log.debug("LOADED session "+data);
807 }
808 return data;
809 }
810 finally
811 {
812 if (connection!=null)
813 connection.close();
814 }
815 }
816
817
818
819
820
821
822
823 protected void storeSession (SessionData data)
824 throws Exception
825 {
826 if (data==null)
827 return;
828
829
830 Connection connection = getConnection();
831 PreparedStatement statement = null;
832 try
833 {
834 String rowId = calculateRowId(data);
835
836 long now = System.currentTimeMillis();
837 connection.setAutoCommit(true);
838 statement = connection.prepareStatement(__insertSession);
839 statement.setString(1, rowId);
840 statement.setString(2, data.getId());
841 statement.setString(3, data.getCanonicalContext());
842 statement.setString(4, data.getVirtualHost());
843 statement.setString(5, getIdManager().getWorkerName());
844 statement.setLong(6, data.getAccessed());
845 statement.setLong(7, data.getLastAccessed());
846 statement.setLong(8, data.getCreated());
847 statement.setLong(9, data.getCookieSet());
848 statement.setLong(10, now);
849 statement.setLong(11, data.getExpiryTime());
850
851 ByteArrayOutputStream baos = new ByteArrayOutputStream();
852 ObjectOutputStream oos = new ObjectOutputStream(baos);
853 oos.writeObject(data.getAttributeMap());
854 byte[] bytes = baos.toByteArray();
855
856 ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
857 statement.setBinaryStream(12, bais, bytes.length);
858
859 statement.executeUpdate();
860 data.setRowId(rowId);
861 data.setLastSaved(now);
862
863
864 if (Log.isDebugEnabled())
865 Log.debug("Stored session "+data);
866 }
867 finally
868 {
869 if (connection!=null)
870 connection.close();
871 }
872 }
873
874
875
876
877
878
879
880
881 protected void updateSession (SessionData data)
882 throws Exception
883 {
884 if (data==null)
885 return;
886
887 Connection connection = getConnection();
888 PreparedStatement statement = null;
889 try
890 {
891 long now = System.currentTimeMillis();
892 connection.setAutoCommit(true);
893 statement = connection.prepareStatement(__updateSession);
894 statement.setString(1, getIdManager().getWorkerName());
895 statement.setLong(2, data.getAccessed());
896 statement.setLong(3, data.getLastAccessed());
897 statement.setLong(4, now);
898 statement.setLong(5, data.getExpiryTime());
899
900 ByteArrayOutputStream baos = new ByteArrayOutputStream();
901 ObjectOutputStream oos = new ObjectOutputStream(baos);
902 oos.writeObject(data.getAttributeMap());
903 byte[] bytes = baos.toByteArray();
904 ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
905
906 statement.setBinaryStream(6, bais, bytes.length);
907 statement.setString(7, data.getRowId());
908 statement.executeUpdate();
909
910 data.setLastSaved(now);
911 if (Log.isDebugEnabled())
912 Log.debug("Updated session "+data);
913 }
914 finally
915 {
916 if (connection!=null)
917 connection.close();
918 }
919 }
920
921
922
923
924
925
926
927
928 protected void updateSessionNode (SessionData data)
929 throws Exception
930 {
931 String nodeId = getIdManager().getWorkerName();
932 Connection connection = getConnection();
933 PreparedStatement statement = null;
934 try
935 {
936 connection.setAutoCommit(true);
937 statement = connection.prepareStatement(__updateSessionNode);
938 statement.setString(1, nodeId);
939 statement.setString(2, data.getRowId());
940 statement.executeUpdate();
941 statement.close();
942 if (Log.isDebugEnabled())
943 Log.debug("Updated last node for session id="+data.getId()+", lastNode = "+nodeId);
944 }
945 finally
946 {
947 if (connection!=null)
948 connection.close();
949 }
950 }
951
952
953
954
955
956
957
958 private void updateSessionAccessTime (SessionData data)
959 throws Exception
960 {
961 Connection connection = getConnection();
962 PreparedStatement statement = null;
963 try
964 {
965 long now = System.currentTimeMillis();
966 connection.setAutoCommit(true);
967 statement = connection.prepareStatement(__updateSessionAccessTime);
968 statement.setString(1, getIdManager().getWorkerName());
969 statement.setLong(2, data.getAccessed());
970 statement.setLong(3, data.getLastAccessed());
971 statement.setLong(4, now);
972 statement.setLong(5, data.getExpiryTime());
973 statement.setString(6, data.getRowId());
974 statement.executeUpdate();
975 data.setLastSaved(now);
976 statement.close();
977 if (Log.isDebugEnabled())
978 Log.debug("Updated access time session id="+data.getId());
979 }
980 finally
981 {
982 if (connection!=null)
983 connection.close();
984 }
985 }
986
987
988
989
990
991
992
993
994
995
996
997 protected void deleteSession (SessionData data)
998 throws Exception
999 {
1000 Connection connection = getConnection();
1001 PreparedStatement statement = null;
1002 try
1003 {
1004 connection.setAutoCommit(true);
1005 statement = connection.prepareStatement(__deleteSession);
1006 statement.setString(1, data.getRowId());
1007 statement.executeUpdate();
1008 if (Log.isDebugEnabled())
1009 Log.debug("Deleted Session "+data);
1010 }
1011 finally
1012 {
1013 if (connection!=null)
1014 connection.close();
1015 }
1016 }
1017
1018
1019
1020
1021
1022
1023
1024
1025 private Connection getConnection ()
1026 throws SQLException
1027 {
1028 return ((JDBCSessionIdManager)getIdManager()).getConnection();
1029 }
1030
1031
1032
1033
1034
1035
1036
1037
1038 private String calculateRowId (SessionData data)
1039 {
1040 String rowId = canonicalize(_context.getContextPath());
1041 rowId = rowId + "_" + getVirtualHost(_context);
1042 rowId = rowId+"_"+data.getId();
1043 return rowId;
1044 }
1045
1046
1047
1048
1049
1050
1051
1052
1053 private String getVirtualHost (ContextHandler.Context context)
1054 {
1055 String vhost = "0.0.0.0";
1056
1057 if (context==null)
1058 return vhost;
1059
1060 String [] vhosts = context.getContextHandler().getVirtualHosts();
1061 if (vhosts==null || vhosts.length==0 || vhosts[0]==null)
1062 return vhost;
1063
1064 return vhosts[0];
1065 }
1066
1067
1068
1069
1070
1071
1072
1073 private String canonicalize (String path)
1074 {
1075 if (path==null)
1076 return "";
1077
1078 return path.replace('/', '_').replace('.','_').replace('\\','_');
1079 }
1080 }