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