1 package org.apache.commons.jcs.auxiliary.disk.jdbc;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import java.io.IOException;
23 import java.sql.Connection;
24 import java.sql.PreparedStatement;
25 import java.sql.ResultSet;
26 import java.sql.SQLException;
27 import java.sql.Timestamp;
28 import java.util.HashMap;
29 import java.util.List;
30 import java.util.Map;
31 import java.util.Set;
32 import java.util.concurrent.atomic.AtomicInteger;
33
34 import javax.sql.DataSource;
35
36 import org.apache.commons.jcs.auxiliary.AuxiliaryCacheAttributes;
37 import org.apache.commons.jcs.auxiliary.disk.AbstractDiskCache;
38 import org.apache.commons.jcs.auxiliary.disk.jdbc.dsfactory.DataSourceFactory;
39 import org.apache.commons.jcs.engine.CacheConstants;
40 import org.apache.commons.jcs.engine.behavior.ICacheElement;
41 import org.apache.commons.jcs.engine.behavior.ICompositeCacheManager;
42 import org.apache.commons.jcs.engine.behavior.IElementSerializer;
43 import org.apache.commons.jcs.engine.logging.behavior.ICacheEvent;
44 import org.apache.commons.jcs.engine.logging.behavior.ICacheEventLogger;
45 import org.apache.commons.jcs.engine.stats.StatElement;
46 import org.apache.commons.jcs.engine.stats.behavior.IStatElement;
47 import org.apache.commons.jcs.engine.stats.behavior.IStats;
48 import org.apache.commons.jcs.utils.serialization.StandardSerializer;
49 import org.apache.commons.logging.Log;
50 import org.apache.commons.logging.LogFactory;
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82 public class JDBCDiskCache<K, V>
83 extends AbstractDiskCache<K, V>
84 {
85
86 private static final Log log = LogFactory.getLog( JDBCDiskCache.class );
87
88
89 private IElementSerializer elementSerializer = new StandardSerializer();
90
91
92 private JDBCDiskCacheAttributes jdbcDiskCacheAttributes;
93
94
95 private AtomicInteger updateCount = new AtomicInteger(0);
96
97
98 private AtomicInteger getCount = new AtomicInteger(0);
99
100
101 private AtomicInteger getMatchingCount = new AtomicInteger(0);
102
103
104 private static final int LOG_INTERVAL = 100;
105
106
107 private DataSourceFactory dsFactory = null;
108
109
110 private TableState tableState;
111
112
113
114
115
116
117
118
119
120
121 public JDBCDiskCache( JDBCDiskCacheAttributes cattr, DataSourceFactory dsFactory, TableState tableState,
122 ICompositeCacheManager compositeCacheManager )
123 {
124 super( cattr );
125
126 setTableState( tableState );
127 setJdbcDiskCacheAttributes( cattr );
128
129 if ( log.isInfoEnabled() )
130 {
131 log.info( "jdbcDiskCacheAttributes = " + getJdbcDiskCacheAttributes() );
132 }
133
134
135 this.dsFactory = dsFactory;
136
137
138 setAlive(true);
139 }
140
141
142
143
144
145
146
147
148 @Override
149 protected void processUpdate( ICacheElement<K, V> ce )
150 {
151 updateCount.incrementAndGet();
152
153 if ( log.isDebugEnabled() )
154 {
155 log.debug( "updating, ce = " + ce );
156 }
157
158 Connection con;
159 try
160 {
161 con = getDataSource().getConnection();
162 }
163 catch ( SQLException e )
164 {
165 log.error( "Problem getting connection.", e );
166 return;
167 }
168
169 try
170 {
171 if ( log.isDebugEnabled() )
172 {
173 log.debug( "Putting [" + ce.getKey() + "] on disk." );
174 }
175
176 byte[] element;
177
178 try
179 {
180 element = getElementSerializer().serialize( ce );
181 }
182 catch ( IOException e )
183 {
184 log.error( "Could not serialize element", e );
185 return;
186 }
187
188 insertOrUpdate( ce, con, element );
189 }
190 finally
191 {
192 try
193 {
194 con.close();
195 }
196 catch ( SQLException e )
197 {
198 log.error( "Problem closing connection.", e );
199 }
200 }
201
202 if ( log.isInfoEnabled() )
203 {
204 if ( updateCount.get() % LOG_INTERVAL == 0 )
205 {
206
207 log.info( "Update Count [" + updateCount + "]" );
208 }
209 }
210 }
211
212
213
214
215
216
217
218
219
220
221 private void insertOrUpdate( ICacheElement<K, V> ce, Connection con, byte[] element )
222 {
223 boolean exists = false;
224
225
226 if ( this.getJdbcDiskCacheAttributes().isTestBeforeInsert() )
227 {
228 exists = doesElementExist( ce, con );
229 }
230
231
232 if ( !exists )
233 {
234 exists = insertRow( ce, con, element );
235 }
236
237
238 if ( exists )
239 {
240 updateRow( ce, con, element );
241 }
242 }
243
244
245
246
247
248
249
250
251
252 private boolean insertRow( ICacheElement<K, V> ce, Connection con, byte[] element )
253 {
254 boolean exists = false;
255 PreparedStatement psInsert = null;
256
257 try
258 {
259 String sqlI = "insert into "
260 + getJdbcDiskCacheAttributes().getTableName()
261 + " (CACHE_KEY, REGION, ELEMENT, MAX_LIFE_SECONDS, IS_ETERNAL, CREATE_TIME, UPDATE_TIME_SECONDS, SYSTEM_EXPIRE_TIME_SECONDS) "
262 + " values (?, ?, ?, ?, ?, ?, ?, ?)";
263
264 psInsert = con.prepareStatement( sqlI );
265 psInsert.setString( 1, (String) ce.getKey() );
266 psInsert.setString( 2, this.getCacheName() );
267 psInsert.setBytes( 3, element );
268 psInsert.setLong( 4, ce.getElementAttributes().getMaxLife() );
269 if ( ce.getElementAttributes().getIsEternal() )
270 {
271 psInsert.setString( 5, "T" );
272 }
273 else
274 {
275 psInsert.setString( 5, "F" );
276 }
277 Timestamp createTime = new Timestamp( ce.getElementAttributes().getCreateTime() );
278 psInsert.setTimestamp( 6, createTime );
279
280 long now = System.currentTimeMillis() / 1000;
281 psInsert.setLong( 7, now );
282
283 long expireTime = now + ce.getElementAttributes().getMaxLife();
284 psInsert.setLong( 8, expireTime );
285
286 psInsert.execute();
287 }
288 catch ( SQLException e )
289 {
290 if ("23000".equals(e.getSQLState()))
291 {
292 exists = true;
293 }
294 else
295 {
296 log.error( "Could not insert element", e );
297 }
298
299
300 if ( !exists && !this.getJdbcDiskCacheAttributes().isTestBeforeInsert() )
301 {
302 exists = doesElementExist( ce, con );
303 }
304 }
305 finally
306 {
307 if (psInsert != null)
308 {
309 try
310 {
311 psInsert.close();
312 }
313 catch (SQLException e)
314 {
315 log.error( "Problem closing statement.", e );
316 }
317 }
318 }
319
320 return exists;
321 }
322
323
324
325
326
327
328
329
330 private void updateRow( ICacheElement<K, V> ce, Connection con, byte[] element )
331 {
332 String sqlU = null;
333 PreparedStatement psUpdate = null;
334
335 try
336 {
337 sqlU = "update " + getJdbcDiskCacheAttributes().getTableName()
338 + " set ELEMENT = ?, CREATE_TIME = ?, UPDATE_TIME_SECONDS = ?, " + " SYSTEM_EXPIRE_TIME_SECONDS = ? "
339 + " where CACHE_KEY = ? and REGION = ?";
340 psUpdate = con.prepareStatement( sqlU );
341 psUpdate.setBytes( 1, element );
342
343 Timestamp createTime = new Timestamp( ce.getElementAttributes().getCreateTime() );
344 psUpdate.setTimestamp( 2, createTime );
345
346 long now = System.currentTimeMillis() / 1000;
347 psUpdate.setLong( 3, now );
348
349 long expireTime = now + ce.getElementAttributes().getMaxLife();
350 psUpdate.setLong( 4, expireTime );
351
352 psUpdate.setString( 5, (String) ce.getKey() );
353 psUpdate.setString( 6, this.getCacheName() );
354 psUpdate.execute();
355
356 if ( log.isDebugEnabled() )
357 {
358 log.debug( "ran update " + sqlU );
359 }
360 }
361 catch ( SQLException e2 )
362 {
363 log.error( "e2 sql [" + sqlU + "] Exception: ", e2 );
364 }
365 finally
366 {
367 if (psUpdate != null)
368 {
369 try
370 {
371 psUpdate.close();
372 }
373 catch (SQLException e)
374 {
375 log.error( "Problem closing statement.", e );
376 }
377 }
378 }
379 }
380
381
382
383
384
385
386
387
388 protected boolean doesElementExist( ICacheElement<K, V> ce, Connection con )
389 {
390 boolean exists = false;
391 PreparedStatement psSelect = null;
392 ResultSet rs = null;
393
394 try
395 {
396
397 String sqlS = "select CACHE_KEY from " + getJdbcDiskCacheAttributes().getTableName()
398 + " where REGION = ? and CACHE_KEY = ?";
399
400 psSelect = con.prepareStatement( sqlS );
401 psSelect.setString( 1, this.getCacheName() );
402 psSelect.setString( 2, (String) ce.getKey() );
403
404 rs = psSelect.executeQuery();
405
406 if ( rs.next() )
407 {
408 exists = true;
409 }
410
411 if ( log.isDebugEnabled() )
412 {
413 log.debug( "[" + ce.getKey() + "] existing status is " + exists );
414 }
415 }
416 catch ( SQLException e )
417 {
418 log.error( "Problem looking for item before insert.", e );
419 }
420 finally
421 {
422 try
423 {
424 if ( rs != null )
425 {
426 rs.close();
427 }
428 }
429 catch ( SQLException e )
430 {
431 log.error( "Problem closing result set.", e );
432 }
433 try
434 {
435 if ( psSelect != null )
436 {
437 psSelect.close();
438 }
439 }
440 catch ( SQLException e )
441 {
442 log.error( "Problem closing statement.", e );
443 }
444 }
445
446 return exists;
447 }
448
449
450
451
452
453
454
455
456 @Override
457 protected ICacheElement<K, V> processGet( K key )
458 {
459 getCount.incrementAndGet();
460
461 if ( log.isDebugEnabled() )
462 {
463 log.debug( "Getting [" + key + "] from disk" );
464 }
465
466 if ( !isAlive() )
467 {
468 return null;
469 }
470
471 ICacheElement<K, V> obj = null;
472
473 byte[] data = null;
474 try
475 {
476
477 String selectString = "select ELEMENT from " + getJdbcDiskCacheAttributes().getTableName()
478 + " where REGION = ? and CACHE_KEY = ?";
479
480 Connection con = getDataSource().getConnection();
481 try
482 {
483 PreparedStatement psSelect = null;
484
485 try
486 {
487 psSelect = con.prepareStatement( selectString );
488 psSelect.setString( 1, this.getCacheName() );
489 psSelect.setString( 2, key.toString() );
490
491 ResultSet rs = psSelect.executeQuery();
492
493 try
494 {
495 if ( rs.next() )
496 {
497 data = rs.getBytes( 1 );
498 }
499 if ( data != null )
500 {
501 try
502 {
503
504 obj = getElementSerializer().deSerialize( data, null );
505 }
506 catch ( IOException ioe )
507 {
508 log.error( "Problem getting item for key [" + key + "]", ioe );
509 }
510 catch ( Exception e )
511 {
512 log.error( "Problem getting item for key [" + key + "]", e );
513 }
514 }
515 }
516 finally
517 {
518 if ( rs != null )
519 {
520 rs.close();
521 }
522 }
523 }
524 finally
525 {
526 if ( psSelect != null )
527 {
528 psSelect.close();
529 }
530 }
531 }
532 finally
533 {
534 if ( con != null )
535 {
536 con.close();
537 }
538 }
539 }
540 catch ( SQLException sqle )
541 {
542 log.error( "Caught a SQL exception trying to get the item for key [" + key + "]", sqle );
543 }
544
545 if ( log.isInfoEnabled() )
546 {
547 if ( getCount.get() % LOG_INTERVAL == 0 )
548 {
549
550 log.info( "Get Count [" + getCount + "]" );
551 }
552 }
553 return obj;
554 }
555
556
557
558
559
560
561
562
563 @Override
564 protected Map<K, ICacheElement<K, V>> processGetMatching( String pattern )
565 {
566 getMatchingCount.incrementAndGet();
567
568 if ( log.isDebugEnabled() )
569 {
570 log.debug( "Getting [" + pattern + "] from disk" );
571 }
572
573 if ( !isAlive() )
574 {
575 return null;
576 }
577
578 Map<K, ICacheElement<K, V>> results = new HashMap<K, ICacheElement<K, V>>();
579
580 try
581 {
582
583 String selectString = "select CACHE_KEY, ELEMENT from " + getJdbcDiskCacheAttributes().getTableName()
584 + " where REGION = ? and CACHE_KEY like ?";
585
586 Connection con = getDataSource().getConnection();
587 try
588 {
589 PreparedStatement psSelect = null;
590 try
591 {
592 psSelect = con.prepareStatement( selectString );
593 psSelect.setString( 1, this.getCacheName() );
594 psSelect.setString( 2, constructLikeParameterFromPattern( pattern ) );
595
596 ResultSet rs = psSelect.executeQuery();
597 try
598 {
599 while ( rs.next() )
600 {
601 String key = rs.getString( 1 );
602 byte[] data = rs.getBytes( 2 );
603 if ( data != null )
604 {
605 try
606 {
607
608 ICacheElement<K, V> value = getElementSerializer().deSerialize( data, null );
609 results.put( (K) key, value );
610 }
611 catch ( IOException ioe )
612 {
613 log.error( "Problem getting items for pattern [" + pattern + "]", ioe );
614 }
615 catch ( Exception e )
616 {
617 log.error( "Problem getting items for pattern [" + pattern + "]", e );
618 }
619 }
620 }
621 }
622 finally
623 {
624 if ( rs != null )
625 {
626 rs.close();
627 }
628 }
629 }
630 finally
631 {
632 if ( psSelect != null )
633 {
634 psSelect.close();
635 }
636 }
637 }
638 finally
639 {
640 if ( con != null )
641 {
642 con.close();
643 }
644 }
645 }
646 catch ( SQLException sqle )
647 {
648 log.error( "Caught a SQL exception trying to get items for pattern [" + pattern + "]", sqle );
649 }
650
651 if ( log.isInfoEnabled() )
652 {
653 if ( getMatchingCount.get() % LOG_INTERVAL == 0 )
654 {
655
656 log.info( "Get Matching Count [" + getMatchingCount + "]" );
657 }
658 }
659 return results;
660 }
661
662
663
664
665
666 public String constructLikeParameterFromPattern( String pattern )
667 {
668 String likePattern = pattern.replaceAll( "\\.\\+", "%" );
669 likePattern = likePattern.replaceAll( "\\.", "_" );
670
671 if ( log.isDebugEnabled() )
672 {
673 log.debug( "pattern = [" + likePattern + "]" );
674 }
675
676 return likePattern;
677 }
678
679
680
681
682
683
684
685
686 @Override
687 protected boolean processRemove( K key )
688 {
689
690 String sql = "delete from " + getJdbcDiskCacheAttributes().getTableName()
691 + " where REGION = ? and CACHE_KEY = ?";
692
693 try
694 {
695 boolean partial = false;
696 if ( key instanceof String && key.toString().endsWith( CacheConstants.NAME_COMPONENT_DELIMITER ) )
697 {
698
699 sql = "delete from " + getJdbcDiskCacheAttributes().getTableName()
700 + " where REGION = ? and CACHE_KEY like ?";
701 partial = true;
702 }
703 Connection con = getDataSource().getConnection();
704 PreparedStatement psSelect = null;
705 try
706 {
707 psSelect = con.prepareStatement( sql );
708 psSelect.setString( 1, this.getCacheName() );
709 if ( partial )
710 {
711 psSelect.setString( 2, key.toString() + "%" );
712 }
713 else
714 {
715 psSelect.setString( 2, key.toString() );
716 }
717
718 psSelect.executeUpdate();
719
720 setAlive(true);
721 }
722 catch ( SQLException e )
723 {
724 log.error( "Problem creating statement. sql [" + sql + "]", e );
725 setAlive(false);
726 }
727 finally
728 {
729 try
730 {
731 if ( psSelect != null )
732 {
733 psSelect.close();
734 }
735 con.close();
736 }
737 catch ( SQLException e1 )
738 {
739 log.error( "Problem closing statement.", e1 );
740 }
741 }
742 }
743 catch ( SQLException e )
744 {
745 log.error( "Problem updating cache.", e );
746 reset();
747 }
748 return false;
749 }
750
751
752
753
754
755 @Override
756 protected void processRemoveAll()
757 {
758
759 if ( this.jdbcDiskCacheAttributes.isAllowRemoveAll() )
760 {
761 try
762 {
763 String sql = "delete from " + getJdbcDiskCacheAttributes().getTableName() + " where REGION = ?";
764 Connection con = getDataSource().getConnection();
765 PreparedStatement psDelete = null;
766 try
767 {
768 psDelete = con.prepareStatement( sql );
769 psDelete.setString( 1, this.getCacheName() );
770 setAlive(true);
771 psDelete.executeUpdate();
772 }
773 catch ( SQLException e )
774 {
775 log.error( "Problem creating statement.", e );
776 setAlive(false);
777 }
778 finally
779 {
780 try
781 {
782 if ( psDelete != null )
783 {
784 psDelete.close();
785 }
786 con.close();
787 }
788 catch ( SQLException e1 )
789 {
790 log.error( "Problem closing statement.", e1 );
791 }
792 }
793 }
794 catch ( Exception e )
795 {
796 log.error( "Problem removing all.", e );
797 reset();
798 }
799 }
800 else
801 {
802 if ( log.isInfoEnabled() )
803 {
804 log.info( "RemoveAll was requested but the request was not fulfilled: allowRemoveAll is set to false." );
805 }
806 }
807 }
808
809
810
811
812
813
814 protected int deleteExpired()
815 {
816 int deleted = 0;
817
818 try
819 {
820 getTableState().setState( TableState.DELETE_RUNNING );
821
822 long now = System.currentTimeMillis() / 1000;
823
824
825
826
827
828
829
830 String sql = "delete from " + getJdbcDiskCacheAttributes().getTableName()
831 + " where IS_ETERNAL = ? and REGION = ? and ? > SYSTEM_EXPIRE_TIME_SECONDS";
832
833 Connection con = getDataSource().getConnection();
834 PreparedStatement psDelete = null;
835 try
836 {
837 psDelete = con.prepareStatement( sql );
838 psDelete.setString( 1, "F" );
839 psDelete.setString( 2, this.getCacheName() );
840 psDelete.setLong( 3, now );
841
842 setAlive(true);
843
844 deleted = psDelete.executeUpdate();
845 }
846 catch ( SQLException e )
847 {
848 log.error( "Problem creating statement.", e );
849 setAlive(false);
850 }
851 finally
852 {
853 try
854 {
855 if ( psDelete != null )
856 {
857 psDelete.close();
858 }
859 con.close();
860 }
861 catch ( SQLException e1 )
862 {
863 log.error( "Problem closing statement.", e1 );
864 }
865 }
866 logApplicationEvent( getAuxiliaryCacheAttributes().getName(), "deleteExpired",
867 "Deleted expired elements. URL: " + getDiskLocation() );
868 }
869 catch ( Exception e )
870 {
871 logError( getAuxiliaryCacheAttributes().getName(), "deleteExpired", e.getMessage() + " URL: "
872 + getDiskLocation() );
873 log.error( "Problem removing expired elements from the table.", e );
874 reset();
875 }
876 finally
877 {
878 getTableState().setState( TableState.FREE );
879 }
880
881 return deleted;
882 }
883
884
885
886
887 public void reset()
888 {
889
890 }
891
892
893 @Override
894 public void processDispose()
895 {
896 ICacheEvent<K> cacheEvent = createICacheEvent( getCacheName(), (K)"none", ICacheEventLogger.DISPOSE_EVENT );
897 try
898 {
899 try
900 {
901 dsFactory.close();
902 }
903 catch ( SQLException e )
904 {
905 log.error( "Problem shutting down.", e );
906 }
907 }
908 finally
909 {
910 logICacheEvent( cacheEvent );
911 }
912 }
913
914
915
916
917
918
919 @Override
920 public int getSize()
921 {
922 int size = 0;
923
924
925 String selectString = "select count(*) from " + getJdbcDiskCacheAttributes().getTableName()
926 + " where REGION = ?";
927
928 Connection con;
929 try
930 {
931 con = getDataSource().getConnection();
932 }
933 catch ( SQLException e )
934 {
935 log.error( "Problem getting connection.", e );
936 return size;
937 }
938
939 try
940 {
941 PreparedStatement psSelect = null;
942 try
943 {
944 psSelect = con.prepareStatement( selectString );
945 psSelect.setString( 1, this.getCacheName() );
946 ResultSet rs = null;
947
948 rs = psSelect.executeQuery();
949 try
950 {
951 if ( rs.next() )
952 {
953 size = rs.getInt( 1 );
954 }
955 }
956 finally
957 {
958 if ( rs != null )
959 {
960 rs.close();
961 }
962 }
963 }
964 finally
965 {
966 if ( psSelect != null )
967 {
968 psSelect.close();
969 }
970 }
971 }
972 catch ( SQLException e )
973 {
974 log.error( "Problem getting size.", e );
975 }
976 finally
977 {
978 try
979 {
980 con.close();
981 }
982 catch ( SQLException e )
983 {
984 log.error( "Problem closing connection.", e );
985 }
986 }
987 return size;
988 }
989
990
991
992
993
994
995 @Override
996 public Set<K> getKeySet() throws IOException
997 {
998 throw new UnsupportedOperationException( "Groups not implemented." );
999
1000 }
1001
1002
1003
1004
1005 @Override
1006 public void setElementSerializer( IElementSerializer elementSerializer )
1007 {
1008 this.elementSerializer = elementSerializer;
1009 }
1010
1011
1012
1013
1014 @Override
1015 public IElementSerializer getElementSerializer()
1016 {
1017 return elementSerializer;
1018 }
1019
1020
1021
1022
1023 protected void setJdbcDiskCacheAttributes( JDBCDiskCacheAttributes jdbcDiskCacheAttributes )
1024 {
1025 this.jdbcDiskCacheAttributes = jdbcDiskCacheAttributes;
1026 }
1027
1028
1029
1030
1031 protected JDBCDiskCacheAttributes getJdbcDiskCacheAttributes()
1032 {
1033 return jdbcDiskCacheAttributes;
1034 }
1035
1036
1037
1038
1039 @Override
1040 public AuxiliaryCacheAttributes getAuxiliaryCacheAttributes()
1041 {
1042 return this.getJdbcDiskCacheAttributes();
1043 }
1044
1045
1046
1047
1048
1049
1050 @Override
1051 public IStats getStatistics()
1052 {
1053 IStats stats = super.getStatistics();
1054 stats.setTypeName( "JDBC/Abstract Disk Cache" );
1055
1056 List<IStatElement<?>> elems = stats.getStatElements();
1057
1058 elems.add(new StatElement<AtomicInteger>( "Update Count", updateCount ) );
1059 elems.add(new StatElement<AtomicInteger>( "Get Count", getCount ) );
1060 elems.add(new StatElement<AtomicInteger>( "Get Matching Count", getMatchingCount ) );
1061 elems.add(new StatElement<String>( "DB URL", getJdbcDiskCacheAttributes().getUrl()) );
1062
1063 stats.setStatElements( elems );
1064
1065 return stats;
1066 }
1067
1068
1069
1070
1071
1072
1073 protected String getTableName()
1074 {
1075 String name = "UNDEFINED";
1076 if ( this.getJdbcDiskCacheAttributes() != null )
1077 {
1078 name = this.getJdbcDiskCacheAttributes().getTableName();
1079 }
1080 return name;
1081 }
1082
1083
1084
1085
1086 public void setTableState( TableState tableState )
1087 {
1088 this.tableState = tableState;
1089 }
1090
1091
1092
1093
1094 public TableState getTableState()
1095 {
1096 return tableState;
1097 }
1098
1099
1100
1101
1102
1103
1104 @Override
1105 protected String getDiskLocation()
1106 {
1107 return this.jdbcDiskCacheAttributes.getUrl();
1108 }
1109
1110
1111
1112
1113
1114
1115 public DataSource getDataSource() throws SQLException
1116 {
1117 return dsFactory.getDataSource();
1118 }
1119
1120
1121
1122
1123
1124
1125 @Override
1126 public String toString()
1127 {
1128 return this.getStats();
1129 }
1130 }