1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.eclipse.jetty.server.session;
20
21 import java.io.ByteArrayInputStream;
22 import java.io.InputStream;
23 import java.sql.Blob;
24 import java.sql.Connection;
25 import java.sql.DatabaseMetaData;
26 import java.sql.Driver;
27 import java.sql.DriverManager;
28 import java.sql.PreparedStatement;
29 import java.sql.ResultSet;
30 import java.sql.SQLException;
31 import java.sql.Statement;
32 import java.util.HashSet;
33 import java.util.Locale;
34 import java.util.Random;
35 import java.util.Set;
36 import java.util.Timer;
37 import java.util.TimerTask;
38
39 import javax.naming.InitialContext;
40 import javax.servlet.http.HttpServletRequest;
41 import javax.servlet.http.HttpSession;
42 import javax.sql.DataSource;
43
44 import org.eclipse.jetty.server.Handler;
45 import org.eclipse.jetty.server.Server;
46 import org.eclipse.jetty.server.SessionManager;
47 import org.eclipse.jetty.server.handler.ContextHandler;
48 import org.eclipse.jetty.util.log.Logger;
49
50
51
52
53
54
55
56
57
58
59 public class JDBCSessionIdManager extends AbstractSessionIdManager
60 {
61 final static Logger LOG = SessionHandler.LOG;
62 public final static int MAX_INTERVAL_NOT_SET = -999;
63
64 protected final HashSet<String> _sessionIds = new HashSet<String>();
65 protected Server _server;
66 protected Driver _driver;
67 protected String _driverClassName;
68 protected String _connectionUrl;
69 protected DataSource _datasource;
70 protected String _jndiName;
71 protected String _sessionIdTable = "JettySessionIds";
72 protected String _sessionTable = "JettySessions";
73 protected String _sessionTableRowId = "rowId";
74 protected int _deleteBlockSize = 10;
75
76 protected Timer _timer;
77 protected TimerTask _task;
78 protected long _lastScavengeTime;
79 protected long _scavengeIntervalMs = 1000L * 60 * 10;
80 protected String _blobType;
81 protected String _longType;
82
83 protected String _createSessionIdTable;
84 protected String _createSessionTable;
85
86 protected String _selectBoundedExpiredSessions;
87
88 protected String _insertId;
89 protected String _deleteId;
90 protected String _queryId;
91
92 protected String _insertSession;
93 protected String _deleteSession;
94 protected String _updateSession;
95 protected String _updateSessionNode;
96 protected String _updateSessionAccessTime;
97
98 protected DatabaseAdaptor _dbAdaptor;
99
100 private String _selectExpiredSessions;
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115 public class DatabaseAdaptor
116 {
117 String _dbName;
118 boolean _isLower;
119 boolean _isUpper;
120
121
122
123 public DatabaseAdaptor (DatabaseMetaData dbMeta)
124 throws SQLException
125 {
126 _dbName = dbMeta.getDatabaseProductName().toLowerCase(Locale.ENGLISH);
127 LOG.debug ("Using database {}",_dbName);
128 _isLower = dbMeta.storesLowerCaseIdentifiers();
129 _isUpper = dbMeta.storesUpperCaseIdentifiers();
130 }
131
132
133
134
135
136
137
138
139 public String convertIdentifier (String identifier)
140 {
141 if (_isLower)
142 return identifier.toLowerCase(Locale.ENGLISH);
143 if (_isUpper)
144 return identifier.toUpperCase(Locale.ENGLISH);
145
146 return identifier;
147 }
148
149 public String getDBName ()
150 {
151 return _dbName;
152 }
153
154 public String getBlobType ()
155 {
156 if (_blobType != null)
157 return _blobType;
158
159 if (_dbName.startsWith("postgres"))
160 return "bytea";
161
162 return "blob";
163 }
164
165 public String getLongType ()
166 {
167 if (_longType != null)
168 return _longType;
169
170 if (_dbName.startsWith("oracle"))
171 return "number(20)";
172
173 return "bigint";
174 }
175
176 public InputStream getBlobInputStream (ResultSet result, String columnName)
177 throws SQLException
178 {
179 if (_dbName.startsWith("postgres"))
180 {
181 byte[] bytes = result.getBytes(columnName);
182 return new ByteArrayInputStream(bytes);
183 }
184
185 Blob blob = result.getBlob(columnName);
186 return blob.getBinaryStream();
187 }
188
189
190
191
192
193 public String getRowIdColumnName ()
194 {
195 if (_dbName != null && _dbName.startsWith("oracle"))
196 return "srowId";
197
198 return "rowId";
199 }
200
201
202 public boolean isEmptyStringNull ()
203 {
204 return (_dbName.startsWith("oracle"));
205 }
206
207 public PreparedStatement getLoadStatement (Connection connection, String rowId, String contextPath, String virtualHosts)
208 throws SQLException
209 {
210 if (contextPath == null || "".equals(contextPath))
211 {
212 if (isEmptyStringNull())
213 {
214 PreparedStatement statement = connection.prepareStatement("select * from "+_sessionTable+
215 " where sessionId = ? and contextPath is null and virtualHost = ?");
216 statement.setString(1, rowId);
217 statement.setString(2, virtualHosts);
218
219 return statement;
220 }
221 }
222
223
224
225 PreparedStatement statement = connection.prepareStatement("select * from "+_sessionTable+
226 " where sessionId = ? and contextPath = ? and virtualHost = ?");
227 statement.setString(1, rowId);
228 statement.setString(2, contextPath);
229 statement.setString(3, virtualHosts);
230
231 return statement;
232 }
233 }
234
235
236
237 public JDBCSessionIdManager(Server server)
238 {
239 super();
240 _server=server;
241 }
242
243 public JDBCSessionIdManager(Server server, Random random)
244 {
245 super(random);
246 _server=server;
247 }
248
249
250
251
252
253
254
255 public void setDriverInfo (String driverClassName, String connectionUrl)
256 {
257 _driverClassName=driverClassName;
258 _connectionUrl=connectionUrl;
259 }
260
261
262
263
264
265
266
267 public void setDriverInfo (Driver driverClass, String connectionUrl)
268 {
269 _driver=driverClass;
270 _connectionUrl=connectionUrl;
271 }
272
273
274 public void setDatasource (DataSource ds)
275 {
276 _datasource = ds;
277 }
278
279 public DataSource getDataSource ()
280 {
281 return _datasource;
282 }
283
284 public String getDriverClassName()
285 {
286 return _driverClassName;
287 }
288
289 public String getConnectionUrl ()
290 {
291 return _connectionUrl;
292 }
293
294 public void setDatasourceName (String jndi)
295 {
296 _jndiName=jndi;
297 }
298
299 public String getDatasourceName ()
300 {
301 return _jndiName;
302 }
303
304 public void setBlobType (String name)
305 {
306 _blobType = name;
307 }
308
309 public String getBlobType ()
310 {
311 return _blobType;
312 }
313
314
315
316 public String getLongType()
317 {
318 return _longType;
319 }
320
321 public void setLongType(String longType)
322 {
323 this._longType = longType;
324 }
325
326 public void setDeleteBlockSize (int bsize)
327 {
328 this._deleteBlockSize = bsize;
329 }
330
331 public int getDeleteBlockSize ()
332 {
333 return this._deleteBlockSize;
334 }
335
336 public void setScavengeInterval (long sec)
337 {
338 if (sec<=0)
339 sec=60;
340
341 long old_period=_scavengeIntervalMs;
342 long period=sec*1000L;
343
344 _scavengeIntervalMs=period;
345
346
347
348 long tenPercent = _scavengeIntervalMs/10;
349 if ((System.currentTimeMillis()%2) == 0)
350 _scavengeIntervalMs += tenPercent;
351
352 if (LOG.isDebugEnabled())
353 LOG.debug("Scavenging every "+_scavengeIntervalMs+" ms");
354 if (_timer!=null && (period!=old_period || _task==null))
355 {
356 synchronized (this)
357 {
358 if (_task!=null)
359 _task.cancel();
360 _task = new TimerTask()
361 {
362 @Override
363 public void run()
364 {
365 scavenge();
366 }
367 };
368 _timer.schedule(_task,_scavengeIntervalMs,_scavengeIntervalMs);
369 }
370 }
371 }
372
373 public long getScavengeInterval ()
374 {
375 return _scavengeIntervalMs/1000;
376 }
377
378
379 @Override
380 public void addSession(HttpSession session)
381 {
382 if (session == null)
383 return;
384
385 synchronized (_sessionIds)
386 {
387 String id = ((JDBCSessionManager.Session)session).getClusterId();
388 try
389 {
390 insert(id);
391 _sessionIds.add(id);
392 }
393 catch (Exception e)
394 {
395 LOG.warn("Problem storing session id="+id, e);
396 }
397 }
398 }
399
400
401 public void addSession(String id)
402 {
403 if (id == null)
404 return;
405
406 synchronized (_sessionIds)
407 {
408 try
409 {
410 insert(id);
411 _sessionIds.add(id);
412 }
413 catch (Exception e)
414 {
415 LOG.warn("Problem storing session id="+id, e);
416 }
417 }
418 }
419
420
421
422 @Override
423 public void removeSession(HttpSession session)
424 {
425 if (session == null)
426 return;
427
428 removeSession(((JDBCSessionManager.Session)session).getClusterId());
429 }
430
431
432
433 public void removeSession (String id)
434 {
435
436 if (id == null)
437 return;
438
439 synchronized (_sessionIds)
440 {
441 if (LOG.isDebugEnabled())
442 LOG.debug("Removing sessionid="+id);
443 try
444 {
445 _sessionIds.remove(id);
446 delete(id);
447 }
448 catch (Exception e)
449 {
450 LOG.warn("Problem removing session id="+id, e);
451 }
452 }
453
454 }
455
456
457 @Override
458 public boolean idInUse(String id)
459 {
460 if (id == null)
461 return false;
462
463 String clusterId = getClusterId(id);
464 boolean inUse = false;
465 synchronized (_sessionIds)
466 {
467 inUse = _sessionIds.contains(clusterId);
468 }
469
470
471 if (inUse)
472 return true;
473
474
475 try
476 {
477 return exists(clusterId);
478 }
479 catch (Exception e)
480 {
481 LOG.warn("Problem checking inUse for id="+clusterId, e);
482 return false;
483 }
484 }
485
486
487
488
489
490
491 @Override
492 public void invalidateAll(String id)
493 {
494
495 removeSession(id);
496
497 synchronized (_sessionIds)
498 {
499
500
501 Handler[] contexts = _server.getChildHandlersByClass(ContextHandler.class);
502 for (int i=0; contexts!=null && i<contexts.length; i++)
503 {
504 SessionHandler sessionHandler = ((ContextHandler)contexts[i]).getChildHandlerByClass(SessionHandler.class);
505 if (sessionHandler != null)
506 {
507 SessionManager manager = sessionHandler.getSessionManager();
508
509 if (manager != null && manager instanceof JDBCSessionManager)
510 {
511 ((JDBCSessionManager)manager).invalidateSession(id);
512 }
513 }
514 }
515 }
516 }
517
518
519 @Override
520 public void renewSessionId (String oldClusterId, String oldNodeId, HttpServletRequest request)
521 {
522
523 String newClusterId = newSessionId(request.hashCode());
524
525 synchronized (_sessionIds)
526 {
527 removeSession(oldClusterId);
528 addSession(newClusterId);
529
530
531 Handler[] contexts = _server.getChildHandlersByClass(ContextHandler.class);
532 for (int i=0; contexts!=null && i<contexts.length; i++)
533 {
534 SessionHandler sessionHandler = ((ContextHandler)contexts[i]).getChildHandlerByClass(SessionHandler.class);
535 if (sessionHandler != null)
536 {
537 SessionManager manager = sessionHandler.getSessionManager();
538
539 if (manager != null && manager instanceof JDBCSessionManager)
540 {
541 ((JDBCSessionManager)manager).renewSessionId(oldClusterId, oldNodeId, newClusterId, getNodeId(newClusterId, request));
542 }
543 }
544 }
545 }
546 }
547
548
549
550
551
552
553
554
555 @Override
556 public void doStart()
557 throws Exception
558 {
559 initializeDatabase();
560 prepareTables();
561 super.doStart();
562 if (LOG.isDebugEnabled())
563 LOG.debug("Scavenging interval = "+getScavengeInterval()+" sec");
564 _timer=new Timer("JDBCSessionScavenger", true);
565 setScavengeInterval(getScavengeInterval());
566 }
567
568
569
570
571 @Override
572 public void doStop ()
573 throws Exception
574 {
575 synchronized(this)
576 {
577 if (_task!=null)
578 _task.cancel();
579 _task=null;
580 if (_timer!=null)
581 _timer.cancel();
582 _timer=null;
583 }
584 _sessionIds.clear();
585 super.doStop();
586 }
587
588
589
590
591
592
593
594 protected Connection getConnection ()
595 throws SQLException
596 {
597 if (_datasource != null)
598 return _datasource.getConnection();
599 else
600 return DriverManager.getConnection(_connectionUrl);
601 }
602
603
604
605
606
607
608
609
610
611
612
613
614
615 private void prepareTables()
616 throws SQLException
617 {
618 _createSessionIdTable = "create table "+_sessionIdTable+" (id varchar(120), primary key(id))";
619 _selectBoundedExpiredSessions = "select * from "+_sessionTable+" where lastNode = ? and expiryTime >= ? and expiryTime <= ?";
620 _selectExpiredSessions = "select * from "+_sessionTable+" where expiryTime >0 and expiryTime <= ?";
621
622 _insertId = "insert into "+_sessionIdTable+" (id) values (?)";
623 _deleteId = "delete from "+_sessionIdTable+" where id = ?";
624 _queryId = "select * from "+_sessionIdTable+" where id = ?";
625
626 try (Connection connection = getConnection();
627 Statement statement = connection.createStatement())
628 {
629
630 connection.setAutoCommit(true);
631 DatabaseMetaData metaData = connection.getMetaData();
632 _dbAdaptor = new DatabaseAdaptor(metaData);
633 _sessionTableRowId = _dbAdaptor.getRowIdColumnName();
634
635
636 String tableName = _dbAdaptor.convertIdentifier(_sessionIdTable);
637 try (ResultSet result = metaData.getTables(null, null, tableName, null))
638 {
639 if (!result.next())
640 {
641
642 statement.executeUpdate(_createSessionIdTable);
643 }
644 }
645
646 tableName = _dbAdaptor.convertIdentifier(_sessionTable);
647 try (ResultSet result = metaData.getTables(null, null, tableName, null))
648 {
649 if (!result.next())
650 {
651
652 String blobType = _dbAdaptor.getBlobType();
653 String longType = _dbAdaptor.getLongType();
654 _createSessionTable = "create table "+_sessionTable+" ("+_sessionTableRowId+" varchar(120), sessionId varchar(120), "+
655 " contextPath varchar(60), virtualHost varchar(60), lastNode varchar(60), accessTime "+longType+", "+
656 " lastAccessTime "+longType+", createTime "+longType+", cookieTime "+longType+", "+
657 " lastSavedTime "+longType+", expiryTime "+longType+", maxInterval "+longType+", map "+blobType+", primary key("+_sessionTableRowId+"))";
658 statement.executeUpdate(_createSessionTable);
659 }
660 else
661 {
662
663 ResultSet colResult = null;
664 try
665 {
666 colResult = metaData.getColumns(null, null,_dbAdaptor.convertIdentifier(_sessionTable), _dbAdaptor.convertIdentifier("maxInterval"));
667 }
668 catch (SQLException s)
669 {
670 LOG.warn("Problem checking if "+_sessionTable+" table contains maxInterval column. Ensure table contains column definition: \"maxInterval long not null default -999\"");
671 throw s;
672 }
673 try
674 {
675 if (!colResult.next())
676 {
677 try
678 {
679
680 String longType = _dbAdaptor.getLongType();
681 statement.executeUpdate("alter table "+_sessionTable+" add maxInterval "+longType+" not null default "+MAX_INTERVAL_NOT_SET);
682 }
683 catch (SQLException s)
684 {
685 LOG.warn("Problem adding maxInterval column. Ensure table contains column definition: \"maxInterval long not null default -999\"");
686 throw s;
687 }
688 }
689 }
690 finally
691 {
692 colResult.close();
693 }
694 }
695 }
696
697 String index1 = "idx_"+_sessionTable+"_expiry";
698 String index2 = "idx_"+_sessionTable+"_session";
699
700 boolean index1Exists = false;
701 boolean index2Exists = false;
702 try (ResultSet result = metaData.getIndexInfo(null, null, tableName, false, false))
703 {
704 while (result.next())
705 {
706 String idxName = result.getString("INDEX_NAME");
707 if (index1.equalsIgnoreCase(idxName))
708 index1Exists = true;
709 else if (index2.equalsIgnoreCase(idxName))
710 index2Exists = true;
711 }
712 }
713 if (!index1Exists)
714 statement.executeUpdate("create index "+index1+" on "+_sessionTable+" (expiryTime)");
715 if (!index2Exists)
716 statement.executeUpdate("create index "+index2+" on "+_sessionTable+" (sessionId, contextPath)");
717
718
719 _insertSession = "insert into "+_sessionTable+
720 " ("+_sessionTableRowId+", sessionId, contextPath, virtualHost, lastNode, accessTime, lastAccessTime, createTime, cookieTime, lastSavedTime, expiryTime, maxInterval, map) "+
721 " values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
722
723 _deleteSession = "delete from "+_sessionTable+
724 " where "+_sessionTableRowId+" = ?";
725
726 _updateSession = "update "+_sessionTable+
727 " set sessionId = ?, lastNode = ?, accessTime = ?, lastAccessTime = ?, lastSavedTime = ?, expiryTime = ?, maxInterval = ?, map = ? where "+_sessionTableRowId+" = ?";
728
729 _updateSessionNode = "update "+_sessionTable+
730 " set lastNode = ? where "+_sessionTableRowId+" = ?";
731
732 _updateSessionAccessTime = "update "+_sessionTable+
733 " set lastNode = ?, accessTime = ?, lastAccessTime = ?, lastSavedTime = ?, expiryTime = ?, maxInterval = ? where "+_sessionTableRowId+" = ?";
734
735
736 }
737 }
738
739
740
741
742
743
744
745 private void insert (String id)
746 throws SQLException
747 {
748 try (Connection connection = getConnection();
749 PreparedStatement query = connection.prepareStatement(_queryId))
750 {
751 connection.setAutoCommit(true);
752 query.setString(1, id);
753 try (ResultSet result = query.executeQuery())
754 {
755
756 if (!result.next())
757 {
758 try (PreparedStatement statement = connection.prepareStatement(_insertId))
759 {
760 statement.setString(1, id);
761 statement.executeUpdate();
762 }
763 }
764 }
765 }
766 }
767
768
769
770
771
772
773
774 private void delete (String id)
775 throws SQLException
776 {
777 try (Connection connection = getConnection();
778 PreparedStatement statement = connection.prepareStatement(_deleteId))
779 {
780 connection.setAutoCommit(true);
781 statement.setString(1, id);
782 statement.executeUpdate();
783 }
784 }
785
786
787
788
789
790
791
792
793
794 private boolean exists (String id)
795 throws SQLException
796 {
797 try (Connection connection = getConnection();
798 PreparedStatement statement = connection.prepareStatement(_queryId))
799 {
800 connection.setAutoCommit(true);
801 statement.setString(1, id);
802 try (ResultSet result = statement.executeQuery())
803 {
804 return result.next();
805 }
806 }
807 }
808
809
810
811
812
813
814
815
816
817
818
819
820 private void scavenge ()
821 {
822 Connection connection = null;
823 try
824 {
825 if (LOG.isDebugEnabled())
826 LOG.debug(getWorkerName()+"- Scavenge sweep started at "+System.currentTimeMillis());
827 if (_lastScavengeTime > 0)
828 {
829 connection = getConnection();
830 connection.setAutoCommit(true);
831 Set<String> expiredSessionIds = new HashSet<String>();
832
833
834
835 long lowerBound = (_lastScavengeTime - _scavengeIntervalMs);
836 long upperBound = _lastScavengeTime;
837 if (LOG.isDebugEnabled())
838 LOG.debug (getWorkerName()+"- Pass 1: Searching for sessions expired between "+lowerBound + " and "+upperBound);
839
840 try (PreparedStatement statement = connection.prepareStatement(_selectBoundedExpiredSessions))
841 {
842 statement.setString(1, getWorkerName());
843 statement.setLong(2, lowerBound);
844 statement.setLong(3, upperBound);
845 try (ResultSet result = statement.executeQuery())
846 {
847 while (result.next())
848 {
849 String sessionId = result.getString("sessionId");
850 expiredSessionIds.add(sessionId);
851 if (LOG.isDebugEnabled()) LOG.debug ("Found expired sessionId="+sessionId);
852 }
853 }
854 }
855 scavengeSessions(expiredSessionIds, false);
856
857
858
859 try (PreparedStatement selectExpiredSessions = connection.prepareStatement(_selectExpiredSessions))
860 {
861 expiredSessionIds.clear();
862 upperBound = _lastScavengeTime - (2 * _scavengeIntervalMs);
863 if (upperBound > 0)
864 {
865 if (LOG.isDebugEnabled()) LOG.debug(getWorkerName()+"- Pass 2: Searching for sessions expired before "+upperBound);
866 selectExpiredSessions.setLong(1, upperBound);
867 try (ResultSet result = selectExpiredSessions.executeQuery())
868 {
869 while (result.next())
870 {
871 String sessionId = result.getString("sessionId");
872 String lastNode = result.getString("lastNode");
873 if ((getWorkerName() == null && lastNode == null) || (getWorkerName() != null && getWorkerName().equals(lastNode)))
874 expiredSessionIds.add(sessionId);
875 if (LOG.isDebugEnabled()) LOG.debug ("Found expired sessionId="+sessionId+" last managed by "+getWorkerName());
876 }
877 }
878 scavengeSessions(expiredSessionIds, false);
879 }
880
881
882
883
884
885
886 upperBound = _lastScavengeTime - (3 * _scavengeIntervalMs);
887 expiredSessionIds.clear();
888 if (upperBound > 0)
889 {
890 if (LOG.isDebugEnabled()) LOG.debug(getWorkerName()+"- Pass 3: searching for sessions expired before "+upperBound);
891 selectExpiredSessions.setLong(1, upperBound);
892 try (ResultSet result = selectExpiredSessions.executeQuery())
893 {
894 while (result.next())
895 {
896 String sessionId = result.getString("sessionId");
897 expiredSessionIds.add(sessionId);
898 if (LOG.isDebugEnabled()) LOG.debug ("Found expired sessionId="+sessionId);
899 }
900 }
901 scavengeSessions(expiredSessionIds, true);
902 }
903 }
904 }
905 }
906 catch (Exception e)
907 {
908 if (isRunning())
909 LOG.warn("Problem selecting expired sessions", e);
910 else
911 LOG.ignore(e);
912 }
913 finally
914 {
915 _lastScavengeTime=System.currentTimeMillis();
916 if (LOG.isDebugEnabled()) LOG.debug(getWorkerName()+"- Scavenge sweep ended at "+_lastScavengeTime);
917 if (connection != null)
918 {
919 try
920 {
921 connection.close();
922 }
923 catch (SQLException e)
924 {
925 LOG.warn(e);
926 }
927 }
928 }
929 }
930
931
932
933
934
935 private void scavengeSessions (Set<String> expiredSessionIds, boolean forceDelete)
936 {
937 Set<String> remainingIds = new HashSet<String>(expiredSessionIds);
938 Handler[] contexts = _server.getChildHandlersByClass(ContextHandler.class);
939 for (int i=0; contexts!=null && i<contexts.length; i++)
940 {
941 SessionHandler sessionHandler = ((ContextHandler)contexts[i]).getChildHandlerByClass(SessionHandler.class);
942 if (sessionHandler != null)
943 {
944 SessionManager manager = sessionHandler.getSessionManager();
945 if (manager != null && manager instanceof JDBCSessionManager)
946 {
947 Set<String> successfullyExpiredIds = ((JDBCSessionManager)manager).expire(expiredSessionIds);
948 if (successfullyExpiredIds != null)
949 remainingIds.removeAll(successfullyExpiredIds);
950 }
951 }
952 }
953
954
955 if (!remainingIds.isEmpty() && forceDelete)
956 {
957 LOG.info("Forcibly deleting unrecoverable expired sessions {}", remainingIds);
958 try
959 {
960
961 synchronized (_sessionIds)
962 {
963 _sessionIds.removeAll(remainingIds);
964 }
965
966 cleanExpiredSessionIds(remainingIds);
967 }
968 catch (Exception e)
969 {
970 LOG.warn("Error removing expired session ids", e);
971 }
972 }
973 }
974
975
976
977
978 private void cleanExpiredSessionIds (Set<String> expiredIds)
979 throws Exception
980 {
981 if (expiredIds == null || expiredIds.isEmpty())
982 return;
983
984 String[] ids = expiredIds.toArray(new String[expiredIds.size()]);
985 try (Connection con = getConnection())
986 {
987 con.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);
988 con.setAutoCommit(false);
989
990 int start = 0;
991 int end = 0;
992 int blocksize = _deleteBlockSize;
993 int block = 0;
994
995 try (Statement statement = con.createStatement())
996 {
997 while (end < ids.length)
998 {
999 start = block*blocksize;
1000 if ((ids.length - start) >= blocksize)
1001 end = start + blocksize;
1002 else
1003 end = ids.length;
1004
1005
1006 statement.executeUpdate(fillInClause("delete from "+_sessionIdTable+" where id in ", ids, start, end));
1007
1008 statement.executeUpdate(fillInClause("delete from "+_sessionTable+" where sessionId in ", ids, start, end));
1009 block++;
1010 }
1011 }
1012 catch (Exception e)
1013 {
1014 con.rollback();
1015 throw e;
1016 }
1017 con.commit();
1018 }
1019 }
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029 private String fillInClause (String sql, String[] literals, int start, int end)
1030 throws Exception
1031 {
1032 StringBuffer buff = new StringBuffer();
1033 buff.append(sql);
1034 buff.append("(");
1035 for (int i=start; i<end; i++)
1036 {
1037 buff.append("'"+(literals[i])+"'");
1038 if (i+1<end)
1039 buff.append(",");
1040 }
1041 buff.append(")");
1042 return buff.toString();
1043 }
1044
1045
1046
1047 private void initializeDatabase ()
1048 throws Exception
1049 {
1050 if (_datasource != null)
1051 return;
1052
1053 if (_jndiName!=null)
1054 {
1055 InitialContext ic = new InitialContext();
1056 _datasource = (DataSource)ic.lookup(_jndiName);
1057 }
1058 else if ( _driver != null && _connectionUrl != null )
1059 {
1060 DriverManager.registerDriver(_driver);
1061 }
1062 else if (_driverClassName != null && _connectionUrl != null)
1063 {
1064 Class.forName(_driverClassName);
1065 }
1066 else
1067 throw new IllegalStateException("No database configured for sessions");
1068 }
1069
1070
1071 }