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 @Override
235 public String toString ()
236 {
237 return "Session rowId="+_rowId+",id="+_id+",lastNode="+_lastNode+
238 ",created="+_created+",accessed="+_accessed+
239 ",lastAccessed="+_lastAccessed+",cookieSet="+_cookieSet+
240 "lastSaved="+_lastSaved;
241 }
242 }
243
244
245
246
247
248
249
250
251 public class Session extends AbstractSessionManager.Session
252 {
253 private final SessionData _data;
254 private boolean _dirty=false;
255
256
257
258
259
260
261 protected Session (HttpServletRequest request)
262 {
263
264 super(request);
265 _data = new SessionData(_clusterId);
266 _data.setMaxIdleMs(_dftMaxIdleSecs*1000);
267 _data.setCanonicalContext(canonicalize(_context.getContextPath()));
268 _data.setVirtualHost(getVirtualHost(_context));
269 _data.setExpiryTime(_maxIdleMs < 0 ? 0 : (System.currentTimeMillis() + _maxIdleMs));
270 _values=_data.getAttributeMap();
271 }
272
273
274
275
276
277 protected Session (SessionData data)
278 {
279 super(data.getCreated(), data.getId());
280 _data=data;
281 _data.setMaxIdleMs(_dftMaxIdleSecs*1000);
282 _values=data.getAttributeMap();
283 }
284
285 @Override
286 protected Map newAttributeMap()
287 {
288 return _data.getAttributeMap();
289 }
290
291 @Override
292 public void setAttribute (String name, Object value)
293 {
294 super.setAttribute(name, value);
295 _dirty=true;
296 }
297
298 @Override
299 public void removeAttribute (String name)
300 {
301 super.removeAttribute(name);
302 _dirty=true;
303 }
304
305 @Override
306 protected void cookieSet()
307 {
308 _data.setCookieSet(_data.getAccessed());
309 }
310
311
312
313
314
315
316
317 @Override
318 protected void access(long time)
319 {
320 super.access(time);
321 _data.setLastAccessed(_data.getAccessed());
322 _data.setAccessed(time);
323 _data.setExpiryTime(_maxIdleMs < 0 ? 0 : (time + _maxIdleMs));
324 }
325
326
327
328
329
330 @Override
331 protected void complete()
332 {
333 super.complete();
334 try
335 {
336 if (_dirty)
337 {
338
339
340 willPassivate();
341 updateSession(_data);
342 didActivate();
343 }
344 else if ((_data._accessed - _data._lastSaved) >= (getSaveInterval() * 1000))
345 {
346 updateSessionAccessTime(_data);
347 }
348 }
349 catch (Exception e)
350 {
351 Log.warn("Problem persisting changed session data id="+getId(), e);
352 }
353 finally
354 {
355 _dirty=false;
356 }
357 }
358
359 @Override
360 protected void timeout() throws IllegalStateException
361 {
362 if (Log.isDebugEnabled()) Log.debug("Timing out session id="+getClusterId());
363 super.timeout();
364 }
365 }
366
367
368
369
370
371
372
373
374
375 protected class ClassLoadingObjectInputStream extends ObjectInputStream
376 {
377 public ClassLoadingObjectInputStream(java.io.InputStream in) throws IOException
378 {
379 super(in);
380 }
381
382 public ClassLoadingObjectInputStream () throws IOException
383 {
384 super();
385 }
386
387 @Override
388 public Class resolveClass (java.io.ObjectStreamClass cl) throws IOException, ClassNotFoundException
389 {
390 try
391 {
392 return Class.forName(cl.getName(), false, Thread.currentThread().getContextClassLoader());
393 }
394 catch (ClassNotFoundException e)
395 {
396 return super.resolveClass(cl);
397 }
398 }
399 }
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422 public void setSaveInterval (long sec)
423 {
424 _saveIntervalSec=sec;
425 }
426
427 public long getSaveInterval ()
428 {
429 return _saveIntervalSec;
430 }
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445 public void cacheInvalidate (Session session)
446 {
447
448 }
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467 @Override
468 public Session getSession(String idInCluster)
469 {
470 Session session = (Session)_sessions.get(idInCluster);
471
472 synchronized (this)
473 {
474 try
475 {
476
477
478
479
480
481
482
483
484 SessionData data = null;
485 long now = System.currentTimeMillis();
486 if (Log.isDebugEnabled()) Log.debug("now="+now+
487 " lastSaved="+(session==null?0:session._data._lastSaved)+
488 " interval="+(_saveIntervalSec * 1000)+
489 " difference="+(now - (session==null?0:session._data._lastSaved)));
490
491 if (session==null || ((now - session._data._lastSaved) >= (_saveIntervalSec * 1000)))
492 {
493 data = loadSession(idInCluster, canonicalize(_context.getContextPath()), getVirtualHost(_context));
494 }
495 else
496 {
497 data = session._data;
498 }
499
500 if (data != null)
501 {
502 if (!data.getLastNode().equals(getIdManager().getWorkerName()) || session==null)
503 {
504
505
506 if (data._expiryTime > System.currentTimeMillis())
507 {
508
509 session = new Session(data);
510 _sessions.put(idInCluster, session);
511 session.didActivate();
512
513
514 updateSessionNode(data);
515 }
516 }
517 else
518 if (Log.isDebugEnabled()) Log.debug("Session not stale "+session._data);
519
520 }
521 else
522 {
523
524 session=null;
525 if (Log.isDebugEnabled()) Log.debug("No session in database matching id="+idInCluster);
526 }
527
528 return session;
529 }
530 catch (Exception e)
531 {
532 Log.warn("Unable to load session from database", e);
533 return null;
534 }
535 }
536 }
537
538
539
540
541
542 @Override
543 public Map getSessionMap()
544 {
545 return Collections.unmodifiableMap(_sessions);
546 }
547
548
549
550
551
552
553
554 @Override
555 public int getSessions()
556 {
557 int size = 0;
558 synchronized (this)
559 {
560 size = _sessions.size();
561 }
562 return size;
563 }
564
565
566
567
568
569
570
571 @Override
572 public void doStart() throws Exception
573 {
574 if (_sessionIdManager==null)
575 throw new IllegalStateException("No session id manager defined");
576
577 prepareTables();
578
579 _sessions = new ConcurrentHashMap();
580 super.doStart();
581 }
582
583
584
585
586
587
588
589 @Override
590 public void doStop() throws Exception
591 {
592 _sessions.clear();
593 _sessions = null;
594
595 super.doStop();
596 }
597
598 @Override
599 protected void invalidateSessions()
600 {
601
602
603
604
605
606
607 }
608
609
610
611
612
613
614
615 protected void invalidateSession (String idInCluster)
616 {
617 Session session = null;
618 synchronized (this)
619 {
620 session = (Session)_sessions.get(idInCluster);
621 }
622
623 if (session != null)
624 {
625 session.invalidate();
626 }
627 }
628
629
630
631
632
633
634
635 @Override
636 protected void removeSession(String idInCluster)
637 {
638 Session session = null;
639 synchronized (this)
640 {
641 session = (Session)_sessions.remove(idInCluster);
642 }
643 try
644 {
645 if (session != null)
646 deleteSession(session._data);
647 }
648 catch (Exception e)
649 {
650 Log.warn("Problem deleting session id="+idInCluster, e);
651 }
652 }
653
654
655
656
657
658
659
660 @Override
661 protected void addSession(AbstractSessionManager.Session session)
662 {
663 if (session==null)
664 return;
665
666 synchronized (this)
667 {
668 _sessions.put(session.getClusterId(), session);
669 }
670
671
672
673 try
674 {
675 session.willPassivate();
676 storeSession(((JDBCSessionManager.Session)session)._data);
677 session.didActivate();
678 }
679 catch (Exception e)
680 {
681 Log.warn("Unable to store new session id="+session.getId() , e);
682 }
683 }
684
685
686
687
688
689
690
691 @Override
692 protected AbstractSessionManager.Session newSession(HttpServletRequest request)
693 {
694 return new Session(request);
695 }
696
697
698
699
700
701
702
703 @Override
704 public void removeSession(AbstractSessionManager.Session session, boolean invalidate)
705 {
706
707 boolean removed = false;
708
709 synchronized (this)
710 {
711
712 if (getSession(session.getClusterId()) != null)
713 {
714 removed = true;
715 removeSession(session.getClusterId());
716 }
717 }
718
719 if (removed)
720 {
721
722 _sessionIdManager.removeSession(session);
723
724 if (invalidate)
725 _sessionIdManager.invalidateAll(session.getClusterId());
726
727 if (invalidate && _sessionListeners!=null)
728 {
729 HttpSessionEvent event=new HttpSessionEvent(session);
730 for (int i=LazyList.size(_sessionListeners); i-->0;)
731 ((HttpSessionListener)LazyList.get(_sessionListeners,i)).sessionDestroyed(event);
732 }
733 if (!invalidate)
734 {
735 session.willPassivate();
736 }
737 }
738 }
739
740
741
742
743
744
745
746
747 protected void expire (List sessionIds)
748 {
749
750 if (isStopping() || isStopped())
751 return;
752
753
754 Thread thread=Thread.currentThread();
755 ClassLoader old_loader=thread.getContextClassLoader();
756 ListIterator itor = sessionIds.listIterator();
757
758 try
759 {
760 while (itor.hasNext())
761 {
762 String sessionId = (String)itor.next();
763 if (Log.isDebugEnabled()) Log.debug("Expiring session id "+sessionId);
764
765 Session session = (Session)_sessions.get(sessionId);
766 if (session != null)
767 {
768 session.timeout();
769 itor.remove();
770 }
771 else
772 {
773 if (Log.isDebugEnabled()) Log.debug("Unrecognized session id="+sessionId);
774 }
775 }
776 }
777 catch (Throwable t)
778 {
779 if (t instanceof ThreadDeath)
780 throw ((ThreadDeath)t);
781 else
782 Log.warn("Problem expiring sessions", t);
783 }
784 finally
785 {
786 thread.setContextClassLoader(old_loader);
787 }
788 }
789
790
791 protected void prepareTables ()
792 {
793 __insertSession = "insert into "+((JDBCSessionIdManager)_sessionIdManager)._sessionTable+
794 " (rowId, sessionId, contextPath, virtualHost, lastNode, accessTime, lastAccessTime, createTime, cookieTime, lastSavedTime, expiryTime, map) "+
795 " values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
796
797 __deleteSession = "delete from "+((JDBCSessionIdManager)_sessionIdManager)._sessionTable+
798 " where rowId = ?";
799
800 __selectSession = "select * from "+((JDBCSessionIdManager)_sessionIdManager)._sessionTable+
801 " where sessionId = ? and contextPath = ? and virtualHost = ?";
802
803 __updateSession = "update "+((JDBCSessionIdManager)_sessionIdManager)._sessionTable+
804 " set lastNode = ?, accessTime = ?, lastAccessTime = ?, lastSavedTime = ?, expiryTime = ?, map = ? where rowId = ?";
805
806 __updateSessionNode = "update "+((JDBCSessionIdManager)_sessionIdManager)._sessionTable+
807 " set lastNode = ? where rowId = ?";
808
809 __updateSessionAccessTime = "update "+((JDBCSessionIdManager)_sessionIdManager)._sessionTable+
810 " set lastNode = ?, accessTime = ?, lastAccessTime = ?, lastSavedTime = ?, expiryTime = ? where rowId = ?";
811 }
812
813
814
815
816
817
818
819 protected SessionData loadSession (String id, String canonicalContextPath, String vhost)
820 throws Exception
821 {
822 SessionData data = null;
823 Connection connection = getConnection();
824 PreparedStatement statement = null;
825 try
826 {
827 statement = connection.prepareStatement(__selectSession);
828 statement.setString(1, id);
829 statement.setString(2, canonicalContextPath);
830 statement.setString(3, vhost);
831 ResultSet result = statement.executeQuery();
832 if (result.next())
833 {
834 data = new SessionData(id);
835 data.setRowId(result.getString("rowId"));
836 data.setCookieSet(result.getLong("cookieTime"));
837 data.setLastAccessed(result.getLong("lastAccessTime"));
838 data.setAccessed (result.getLong("accessTime"));
839 data.setCreated(result.getLong("createTime"));
840 data.setLastNode(result.getString("lastNode"));
841 data.setLastSaved(result.getLong("lastSavedTime"));
842 data.setExpiryTime(result.getLong("expiryTime"));
843 data.setCanonicalContext(result.getString("contextPath"));
844 data.setVirtualHost(result.getString("virtualHost"));
845
846 InputStream is = ((JDBCSessionIdManager)getIdManager())._dbAdaptor.getBlobInputStream(result, "map");
847 ClassLoadingObjectInputStream ois = new ClassLoadingObjectInputStream (is);
848 Object o = ois.readObject();
849 data.setAttributeMap((ConcurrentHashMap)o);
850 ois.close();
851
852 if (Log.isDebugEnabled())
853 Log.debug("LOADED session "+data);
854 }
855 return data;
856 }
857 finally
858 {
859 if (connection!=null)
860 connection.close();
861 }
862 }
863
864
865
866
867
868
869
870 protected void storeSession (SessionData data)
871 throws Exception
872 {
873 if (data==null)
874 return;
875
876
877 Connection connection = getConnection();
878 PreparedStatement statement = null;
879 try
880 {
881 String rowId = calculateRowId(data);
882
883 long now = System.currentTimeMillis();
884 connection.setAutoCommit(true);
885 statement = connection.prepareStatement(__insertSession);
886 statement.setString(1, rowId);
887 statement.setString(2, data.getId());
888 statement.setString(3, data.getCanonicalContext());
889 statement.setString(4, data.getVirtualHost());
890 statement.setString(5, getIdManager().getWorkerName());
891 statement.setLong(6, data.getAccessed());
892 statement.setLong(7, data.getLastAccessed());
893 statement.setLong(8, data.getCreated());
894 statement.setLong(9, data.getCookieSet());
895 statement.setLong(10, now);
896 statement.setLong(11, data.getExpiryTime());
897
898 ByteArrayOutputStream baos = new ByteArrayOutputStream();
899 ObjectOutputStream oos = new ObjectOutputStream(baos);
900 oos.writeObject(data.getAttributeMap());
901 byte[] bytes = baos.toByteArray();
902
903 ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
904 statement.setBinaryStream(12, bais, bytes.length);
905
906 statement.executeUpdate();
907 data.setRowId(rowId);
908 data.setLastSaved(now);
909
910
911 if (Log.isDebugEnabled())
912 Log.debug("Stored 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 updateSession (SessionData data)
929 throws Exception
930 {
931 if (data==null)
932 return;
933
934 Connection connection = getConnection();
935 PreparedStatement statement = null;
936 try
937 {
938 long now = System.currentTimeMillis();
939 connection.setAutoCommit(true);
940 statement = connection.prepareStatement(__updateSession);
941 statement.setString(1, getIdManager().getWorkerName());
942 statement.setLong(2, data.getAccessed());
943 statement.setLong(3, data.getLastAccessed());
944 statement.setLong(4, now);
945 statement.setLong(5, data.getExpiryTime());
946
947 ByteArrayOutputStream baos = new ByteArrayOutputStream();
948 ObjectOutputStream oos = new ObjectOutputStream(baos);
949 oos.writeObject(data.getAttributeMap());
950 byte[] bytes = baos.toByteArray();
951 ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
952
953 statement.setBinaryStream(6, bais, bytes.length);
954 statement.setString(7, data.getRowId());
955 statement.executeUpdate();
956
957 data.setLastSaved(now);
958 if (Log.isDebugEnabled())
959 Log.debug("Updated session "+data);
960 }
961 finally
962 {
963 if (connection!=null)
964 connection.close();
965 }
966 }
967
968
969
970
971
972
973
974
975 protected void updateSessionNode (SessionData data)
976 throws Exception
977 {
978 String nodeId = getIdManager().getWorkerName();
979 Connection connection = getConnection();
980 PreparedStatement statement = null;
981 try
982 {
983 connection.setAutoCommit(true);
984 statement = connection.prepareStatement(__updateSessionNode);
985 statement.setString(1, nodeId);
986 statement.setString(2, data.getRowId());
987 statement.executeUpdate();
988 statement.close();
989 if (Log.isDebugEnabled())
990 Log.debug("Updated last node for session id="+data.getId()+", lastNode = "+nodeId);
991 }
992 finally
993 {
994 if (connection!=null)
995 connection.close();
996 }
997 }
998
999
1000
1001
1002
1003
1004
1005 private void updateSessionAccessTime (SessionData data)
1006 throws Exception
1007 {
1008 Connection connection = getConnection();
1009 PreparedStatement statement = null;
1010 try
1011 {
1012 long now = System.currentTimeMillis();
1013 connection.setAutoCommit(true);
1014 statement = connection.prepareStatement(__updateSessionAccessTime);
1015 statement.setString(1, getIdManager().getWorkerName());
1016 statement.setLong(2, data.getAccessed());
1017 statement.setLong(3, data.getLastAccessed());
1018 statement.setLong(4, now);
1019 statement.setLong(5, data.getExpiryTime());
1020 statement.setString(6, data.getRowId());
1021 statement.executeUpdate();
1022 data.setLastSaved(now);
1023 statement.close();
1024 if (Log.isDebugEnabled())
1025 Log.debug("Updated access time session id="+data.getId());
1026 }
1027 finally
1028 {
1029 if (connection!=null)
1030 connection.close();
1031 }
1032 }
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044 protected void deleteSession (SessionData data)
1045 throws Exception
1046 {
1047 Connection connection = getConnection();
1048 PreparedStatement statement = null;
1049 try
1050 {
1051 connection.setAutoCommit(true);
1052 statement = connection.prepareStatement(__deleteSession);
1053 statement.setString(1, data.getRowId());
1054 statement.executeUpdate();
1055 if (Log.isDebugEnabled())
1056 Log.debug("Deleted Session "+data);
1057 }
1058 finally
1059 {
1060 if (connection!=null)
1061 connection.close();
1062 }
1063 }
1064
1065
1066
1067
1068
1069
1070
1071
1072 private Connection getConnection ()
1073 throws SQLException
1074 {
1075 return ((JDBCSessionIdManager)getIdManager()).getConnection();
1076 }
1077
1078
1079
1080
1081
1082
1083
1084
1085 private String calculateRowId (SessionData data)
1086 {
1087 String rowId = canonicalize(_context.getContextPath());
1088 rowId = rowId + "_" + getVirtualHost(_context);
1089 rowId = rowId+"_"+data.getId();
1090 return rowId;
1091 }
1092
1093
1094
1095
1096
1097
1098
1099
1100 private String getVirtualHost (ContextHandler.Context context)
1101 {
1102 String vhost = "0.0.0.0";
1103
1104 if (context==null)
1105 return vhost;
1106
1107 String [] vhosts = context.getContextHandler().getVirtualHosts();
1108 if (vhosts==null || vhosts.length==0 || vhosts[0]==null)
1109 return vhost;
1110
1111 return vhosts[0];
1112 }
1113
1114
1115
1116
1117
1118
1119
1120 private String canonicalize (String path)
1121 {
1122 if (path==null)
1123 return "";
1124
1125 return path.replace('/', '_').replace('.','_').replace('\\','_');
1126 }
1127 }