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