1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.waarp.common.database;
21
22 import io.netty.util.Timeout;
23 import io.netty.util.TimerTask;
24 import org.waarp.common.database.exception.WaarpDatabaseNoConnectionException;
25 import org.waarp.common.database.exception.WaarpDatabaseSqlException;
26 import org.waarp.common.database.model.DbModel;
27 import org.waarp.common.database.model.DbModelFactory;
28 import org.waarp.common.guid.GUID;
29 import org.waarp.common.logging.WaarpLogger;
30 import org.waarp.common.logging.WaarpLoggerFactory;
31 import org.waarp.common.lru.ConcurrentUtility;
32
33 import java.sql.Connection;
34 import java.sql.SQLException;
35 import java.sql.Savepoint;
36 import java.util.ConcurrentModificationException;
37 import java.util.Set;
38 import java.util.concurrent.TimeUnit;
39 import java.util.concurrent.atomic.AtomicInteger;
40
41
42
43
44
45
46
47 public class DbSession {
48
49
50
51 private static final WaarpLogger logger =
52 WaarpLoggerFactory.getLogger(DbSession.class);
53 private static final String CANNOT_CREATE_CONNECTION =
54 "Cannot create Connection";
55 private static final String THREAD_USING = "ThreadUsing: ";
56
57
58
59
60 private DbAdmin admin;
61
62
63
64
65 private Connection conn;
66
67
68
69
70 private boolean isReadOnly = true;
71
72
73
74
75 private boolean autoCommit = true;
76
77
78
79
80 private GUID internalId;
81
82
83
84
85 private final AtomicInteger nbThread = new AtomicInteger(0);
86
87
88
89
90 private boolean isDisActive = true;
91
92
93
94
95
96 private final Set<DbPreparedStatement> listPreparedStatement =
97 ConcurrentUtility.newConcurrentSet();
98
99 private void initialize(final DbModel dbModel, final String server,
100 final String user, final String passwd,
101 final boolean isReadOnly, final boolean autoCommit)
102 throws WaarpDatabaseNoConnectionException {
103 if (!DbModelFactory.classLoaded.contains(dbModel.getDbType().name())) {
104 throw new WaarpDatabaseNoConnectionException("DbAdmin not initialzed");
105 }
106 if (server == null) {
107 setConn(null);
108 logger.error("Cannot set a null Server");
109 throw new WaarpDatabaseNoConnectionException("Cannot set a null Server");
110 }
111 try {
112 setAutoCommit(autoCommit);
113 setConn(dbModel.getDbConnection(server, user, passwd));
114 getConn().setAutoCommit(isAutoCommit());
115 setReadOnly(isReadOnly);
116 getConn().setReadOnly(isReadOnly());
117 setInternalId(new GUID());
118 logger.debug("Open Db Conn: {}", getInternalId());
119 DbAdmin.addConnection(getInternalId(), this);
120 setDisActive(false);
121 checkConnection();
122 } catch (final SQLException ex) {
123 setDisActive(true);
124
125 logger.error(CANNOT_CREATE_CONNECTION + " while already having {}",
126 DbAdmin.getNbConnection());
127 DbConstant.error(ex);
128 if (getConn() != null) {
129 try {
130 getConn().close();
131 } catch (final SQLException ignored) {
132
133 }
134 }
135 setConn(null);
136 throw new WaarpDatabaseNoConnectionException(CANNOT_CREATE_CONNECTION,
137 ex);
138 }
139 }
140
141
142
143
144
145
146
147
148
149
150
151
152
153 public DbSession(final DbAdmin admin, final boolean isReadOnly)
154 throws WaarpDatabaseNoConnectionException {
155 try {
156 setAdmin(admin);
157 initialize(admin.getDbModel(), admin.getServer(), admin.getUser(),
158 admin.getPasswd(), isReadOnly, true);
159 } catch (final NullPointerException ex) {
160
161 setDisActive(true);
162 logger.error(CANNOT_CREATE_CONNECTION + (admin == null), ex);
163 if (getConn() != null) {
164 try {
165 getConn().close();
166 } catch (final SQLException ignored) {
167
168 }
169 }
170 setConn(null);
171 throw new WaarpDatabaseNoConnectionException(CANNOT_CREATE_CONNECTION,
172 ex);
173 }
174 }
175
176
177
178
179
180
181
182
183
184
185
186
187
188 public DbSession(final DbAdmin admin, final boolean isReadOnly,
189 final boolean autoCommit)
190 throws WaarpDatabaseNoConnectionException {
191 try {
192 setAdmin(admin);
193 initialize(admin.getDbModel(), admin.getServer(), admin.getUser(),
194 admin.getPasswd(), isReadOnly, autoCommit);
195 } catch (final NullPointerException ex) {
196
197 logger.error(CANNOT_CREATE_CONNECTION + (admin == null), ex);
198 setDisActive(true);
199 if (getConn() != null) {
200 try {
201 getConn().close();
202 } catch (final SQLException ignored) {
203
204 }
205 }
206 setConn(null);
207 throw new WaarpDatabaseNoConnectionException(CANNOT_CREATE_CONNECTION,
208 ex);
209 }
210 }
211
212
213
214
215
216
217
218
219 public final void setAutoCommit(final boolean autoCommit)
220 throws WaarpDatabaseNoConnectionException {
221 if (getConn() != null) {
222 this.autoCommit = autoCommit;
223 try {
224 getConn().setAutoCommit(autoCommit);
225 } catch (final SQLException e) {
226
227 logger.error(CANNOT_CREATE_CONNECTION + " while already having {}",
228 DbAdmin.getNbConnection());
229 DbConstant.error(e);
230 if (getConn() != null) {
231 try {
232 getConn().close();
233 } catch (final SQLException ignored) {
234
235 }
236 }
237 setConn(null);
238 setDisActive(true);
239 throw new WaarpDatabaseNoConnectionException(CANNOT_CREATE_CONNECTION,
240 e);
241 }
242 }
243 }
244
245
246
247
248 public final DbAdmin getAdmin() {
249 return admin;
250 }
251
252
253
254
255 protected final void setAdmin(final DbAdmin admin) {
256 this.admin = admin;
257 }
258
259
260
261
262
263 public final void useConnection() {
264 final int val = nbThread.incrementAndGet();
265 synchronized (this) {
266 if (isDisActive()) {
267 try {
268 initialize(getAdmin().getDbModel(), getAdmin().getServer(),
269 getAdmin().getUser(), getAdmin().getPasswd(), isReadOnly(),
270 isAutoCommit());
271 } catch (final WaarpDatabaseNoConnectionException e) {
272 logger.error(THREAD_USING + nbThread + " but not connected");
273 return;
274 }
275 }
276 }
277 logger.debug("{}{}", THREAD_USING, val);
278 }
279
280
281
282
283
284 public final void endUseConnection() {
285 final int val = nbThread.decrementAndGet();
286 logger.debug("{}{}", THREAD_USING, val);
287 if (val <= 0) {
288 disconnect();
289 }
290 }
291
292
293
294
295
296 public final void enUseConnectionNoDisconnect() {
297 final int val = nbThread.decrementAndGet();
298 logger.debug("{}{}", THREAD_USING, val);
299 if (val <= 0) {
300 DbAdmin.dbSessionTimer.newTimeout(new TryDisconnectDbSession(this),
301 DbAdmin.WAITFORNETOP * 10,
302 TimeUnit.MILLISECONDS);
303 }
304 }
305
306
307
308
309 private static class TryDisconnectDbSession implements TimerTask {
310 private final DbSession dbSession;
311
312 private TryDisconnectDbSession(final DbSession dbSession) {
313 this.dbSession = dbSession;
314 }
315
316 @Override
317 public final void run(final Timeout timeout) {
318 final int val = dbSession.nbThread.get();
319 if (val <= 0) {
320 dbSession.disconnect();
321 }
322 logger.debug("{}{}", THREAD_USING, val);
323 }
324 }
325
326 @Override
327 public final int hashCode() {
328 return getInternalId().hashCode();
329
330 }
331
332 @Override
333 public final boolean equals(final Object o) {
334 if (!(o instanceof DbSession)) {
335 return false;
336 }
337 return this == o || getInternalId().equals(((DbSession) o).getInternalId());
338 }
339
340
341
342
343 public final void forceDisconnect() {
344 if (getInternalId().equals(getAdmin().getSession().getInternalId())) {
345 logger.debug("Closing internal db connection");
346 }
347 nbThread.set(0);
348 if (getConn() == null) {
349 logger.debug("Connection already closed");
350 return;
351 }
352 logger.debug("DbConnection still in use: {}", nbThread);
353 removeLongTermPreparedStatements();
354 DbAdmin.removeConnection(getInternalId());
355 setDisActive(true);
356 try {
357 logger.debug("Fore close Db Conn: {}", getInternalId());
358 if (getConn() != null) {
359 getConn().close();
360 setConn(null);
361 }
362 } catch (final SQLException e) {
363 logger.warn("Disconnection not OK");
364 DbConstant.error(e);
365 } catch (final ConcurrentModificationException e) {
366
367 }
368 logger.info("Current cached connection: {}",
369 getAdmin().getDbModel().currentNumberOfPooledConnections());
370 }
371
372
373
374
375 public final void disconnect() {
376 if (getInternalId().equals(getAdmin().getSession().getInternalId())) {
377 logger.debug("Closing internal db connection: {}", nbThread.get());
378 }
379 if (getConn() == null || isDisActive()) {
380 logger.debug("Connection already closed");
381 return;
382 }
383 logger.debug("DbConnection still in use: {}", nbThread);
384 if (nbThread.get() > 0) {
385 logger.info("Still some clients could use this Database Session: {}",
386 nbThread);
387 return;
388 }
389 synchronized (this) {
390 removeLongTermPreparedStatements();
391 DbAdmin.removeConnection(getInternalId());
392 setDisActive(true);
393 try {
394 logger.debug("Close Db Conn: {}", getInternalId());
395 if (getConn() != null) {
396 getConn().close();
397 setConn(null);
398 }
399 } catch (final SQLException e) {
400 logger.warn("Disconnection not OK");
401 DbConstant.error(e);
402 } catch (final ConcurrentModificationException e) {
403
404 }
405 }
406 logger.info("Current cached connection: {}",
407 getAdmin().getDbModel().currentNumberOfPooledConnections());
408 }
409
410
411
412
413
414
415 public final void checkConnection()
416 throws WaarpDatabaseNoConnectionException {
417 try {
418 getAdmin().getDbModel().validConnection(this);
419 setDisActive(false);
420 } catch (final WaarpDatabaseNoConnectionException e) {
421 setDisActive(true);
422 throw e;
423 }
424 }
425
426
427
428
429 public final boolean checkConnectionNoException() {
430 try {
431 checkConnection();
432 return true;
433 } catch (final WaarpDatabaseNoConnectionException e) {
434 return false;
435 }
436 }
437
438
439
440
441
442
443 public final void addLongTermPreparedStatement(
444 final DbPreparedStatement longterm) {
445 listPreparedStatement.add(longterm);
446 }
447
448
449
450
451
452
453
454
455 public final void recreateLongTermPreparedStatements()
456 throws WaarpDatabaseNoConnectionException, WaarpDatabaseSqlException {
457 WaarpDatabaseNoConnectionException elast = null;
458 WaarpDatabaseSqlException e2last = null;
459 logger.info("RecreateLongTermPreparedStatements: {}",
460 listPreparedStatement.size());
461 for (final DbPreparedStatement longterm : listPreparedStatement) {
462 try {
463 longterm.recreatePreparedStatement();
464 } catch (final WaarpDatabaseNoConnectionException e) {
465 logger.warn(
466 "Error while recreation of Long Term PreparedStatement" + " : {}",
467 e.getMessage());
468 elast = e;
469 } catch (final WaarpDatabaseSqlException e) {
470 logger.warn(
471 "Error while recreation of Long Term PreparedStatement" + " : {}",
472 e.getMessage());
473 e2last = e;
474 }
475 }
476 if (elast != null) {
477 throw elast;
478 }
479 if (e2last != null) {
480 throw e2last;
481 }
482 }
483
484
485
486
487 public final void removeLongTermPreparedStatements() {
488 for (final DbPreparedStatement longterm : listPreparedStatement) {
489 if (longterm != null) {
490 longterm.realClose();
491 }
492 }
493 listPreparedStatement.clear();
494 }
495
496
497
498
499
500
501 public final void removeLongTermPreparedStatements(
502 final DbPreparedStatement longterm) {
503 listPreparedStatement.remove(longterm);
504 }
505
506
507
508
509
510
511
512 public final void commit()
513 throws WaarpDatabaseSqlException, WaarpDatabaseNoConnectionException {
514 if (getConn() == null) {
515 logger.warn("Cannot commit since connection is null");
516 throw new WaarpDatabaseNoConnectionException(
517 "Cannot commit since connection is null");
518 }
519 if (isAutoCommit()) {
520 return;
521 }
522 if (isDisActive()) {
523 checkConnection();
524 }
525 try {
526 getConn().commit();
527 } catch (final SQLException e) {
528 logger.error("Cannot Commit");
529 DbConstant.error(e);
530 throw new WaarpDatabaseSqlException("Cannot commit", e);
531 }
532 }
533
534
535
536
537
538
539
540
541
542 public final void rollback(final Savepoint savepoint)
543 throws WaarpDatabaseNoConnectionException, WaarpDatabaseSqlException {
544 if (getConn() == null) {
545 logger.warn("Cannot rollback since connection is null");
546 throw new WaarpDatabaseNoConnectionException(
547 "Cannot rollback since connection is null");
548 }
549 if (isDisActive()) {
550 checkConnection();
551 }
552 try {
553 if (savepoint == null) {
554 getConn().rollback();
555 } else {
556 getConn().rollback(savepoint);
557 }
558 } catch (final SQLException e) {
559 logger.error("Cannot rollback");
560 DbConstant.error(e);
561 throw new WaarpDatabaseSqlException("Cannot rollback", e);
562 }
563 }
564
565
566
567
568
569
570
571
572
573 public final Savepoint savepoint()
574 throws WaarpDatabaseNoConnectionException, WaarpDatabaseSqlException {
575 if (getConn() == null) {
576 logger.warn("Cannot savepoint since connection is null");
577 throw new WaarpDatabaseNoConnectionException(
578 "Cannot savepoint since connection is null");
579 }
580 if (isDisActive()) {
581 checkConnection();
582 }
583 try {
584 return getConn().setSavepoint();
585 } catch (final SQLException e) {
586 logger.error("Cannot savepoint");
587 DbConstant.error(e);
588 throw new WaarpDatabaseSqlException("Cannot savepoint", e);
589 }
590 }
591
592
593
594
595
596
597
598
599
600 public final void releaseSavepoint(final Savepoint savepoint)
601 throws WaarpDatabaseNoConnectionException, WaarpDatabaseSqlException {
602 if (getConn() == null) {
603 logger.warn("Cannot release savepoint since connection is null");
604 throw new WaarpDatabaseNoConnectionException(
605 "Cannot release savepoint since connection is null");
606 }
607 if (isDisActive()) {
608 checkConnection();
609 }
610 try {
611 getConn().releaseSavepoint(savepoint);
612 } catch (final SQLException e) {
613 logger.error("Cannot release savepoint");
614 DbConstant.error(e);
615 throw new WaarpDatabaseSqlException("Cannot release savepoint", e);
616 }
617 }
618
619
620
621
622 public final boolean isReadOnly() {
623 return isReadOnly;
624 }
625
626
627
628
629 public final void setReadOnly(final boolean isReadOnly) {
630 this.isReadOnly = isReadOnly;
631 }
632
633
634
635
636 public final boolean isAutoCommit() {
637 return autoCommit;
638 }
639
640
641
642
643 public final Connection getConn() {
644 return conn;
645 }
646
647
648
649
650 public final void setConn(final Connection conn) {
651 this.conn = conn;
652 }
653
654
655
656
657 public final GUID getInternalId() {
658 return internalId;
659 }
660
661
662
663
664 private void setInternalId(final GUID internalId) {
665 this.internalId = internalId;
666 }
667
668
669
670
671 public final boolean isDisActive() {
672 return isDisActive;
673 }
674
675
676
677
678 public final void setDisActive(final boolean isDisActive) {
679 this.isDisActive = isDisActive;
680 }
681 }