001/******************************************************************************* 002The MIT License (MIT) 003 004Copyright (c) 2024 KILLCODING.COM 005 006Permission is hereby granted, free of charge, to any person obtaining a copy 007of this software and associated documentation files (the "Software"), to deal 008in the Software without restriction, including without limitation the rights 009to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 010copies of the Software, and to permit persons to whom the Software is 011furnished to do so, subject to the following conditions: 012 013The above copyright notice and this permission notice shall be included in 014all copies or substantial portions of the Software. 015 016THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 017IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 018FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 019AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 020LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 021OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 022THE SOFTWARE. 023*****************************************************************************/ 024package com.killcoding.datasource; 025 026import java.sql.Connection; 027import java.sql.PreparedStatement; 028import java.util.List; 029import java.sql.SQLException; 030import java.sql.Types; 031import java.util.Map; 032import java.sql.ResultSet; 033import java.sql.ResultSetMetaData; 034import java.util.HashMap; 035import java.util.ArrayList; 036import java.util.regex.Pattern; 037import java.util.regex.Matcher; 038import java.util.Arrays; 039import com.killcoding.tool.ResultMap; 040import java.sql.DatabaseMetaData; 041import com.killcoding.log.LoggerFactory; 042import com.killcoding.log.Logger; 043import java.util.concurrent.Executors; 044import com.killcoding.datasource.DriverConnection; 045import com.killcoding.datasource.DriverDataSource; 046import java.util.concurrent.ConcurrentHashMap; 047import com.killcoding.tool.CommonTools; 048import com.killcoding.tool.ConfigProperties; 049import java.io.File; 050import java.text.DateFormat; 051import java.text.SimpleDateFormat; 052import com.killcoding.tool.FileTools; 053import java.io.IOException; 054import java.nio.file.Files; 055import java.util.Date; 056import java.nio.file.Paths; 057import java.nio.file.Path; 058import java.nio.file.StandardCopyOption; 059import com.killcoding.cache.CacheArray; 060import com.killcoding.cache.CacheArrayFilter; 061import com.killcoding.tool.CodeEscape; 062import java.sql.Blob; 063import java.util.stream.Collectors; 064import java.net.URI; 065import java.util.Comparator; 066import java.sql.Timestamp; 067import java.sql.Clob; 068import java.io.InputStream; 069 070/** 071 * This class is execute sql base class. 072 * Support database replication. 073 * Support database multi-activity. 074 * Support database CRUD 075 * */ 076public class DriverExecutor { 077 078 protected final static Map<Integer, List<DriverExecutor>> SYNC_EXECUTOR_MARK = new ConcurrentHashMap<Integer, List<DriverExecutor>>(); 079 private final static Map<Integer, Long> SYNC_CONN_ERROR_TIME = new ConcurrentHashMap<Integer, Long>(); 080 081 private final static Map<String,String> SQL_LOG_MSG_MAPPING = new ConcurrentHashMap<String,String>(); 082 private final static Map<String,Boolean> SQL_LOG_OVERSPEND_MAPPING = new ConcurrentHashMap<String,Boolean>(); 083 084 protected Logger log = null; 085 086 public final static String COLUMN_NAME_CASE_UPPER = "UPPER"; 087 public final static String COLUMN_NAME_CASE_LOWER = "LOWER"; 088 public final static String COLUMN_NAME_CASE_ORIGINAL = "ORIGINAL"; 089 public static String COLUMN_NAME_CASE_MODE = COLUMN_NAME_CASE_ORIGINAL; 090 091 protected boolean closed = true; 092 protected Connection connection = null; 093 private static CacheArray sqlLogCacheArray = null; 094 095 /** 096 * New a DriverExecutor object 097 * @param connection - JDBC connection 098 * */ 099 public DriverExecutor(Connection connection) { 100 super(); 101 log = LoggerFactory.getLogger(this.getClass()); 102 this.connection = connection; 103 writeSqlLog("open", 0, "open", ""); 104 closed = (this.connection == null); 105 } 106 107 /** 108 * Get current Connection 109 * @return Connection 110 * */ 111 public Connection getConnection() { 112 return connection; 113 } 114 115 /** 116 * Get column classes by table name 117 * @exception SQLException 118 * @return Map<String,Object> - column and java type class mapping 119 * @param tableName - table name 120 * */ 121 public Map<String, Object> getColumnClasses(String tableName) throws SQLException { 122 Map<String, Object> types = null; 123 ResultSet result = null; 124 PreparedStatement statement = null; 125 String sql = String.format("SELECT * FROM %s WHERE 1 = 0", tableName); 126 statement = connection.prepareStatement(sql); 127 result = statement.executeQuery(); 128 final ResultSetMetaData rsmd = result.getMetaData(); 129 for (int i = 0; i < rsmd.getColumnCount(); i++) { 130 if (types == null) { 131 types = new ResultMap<String, Object>(); 132 } 133 String cn = rsmd.getColumnClassName(i + 1); 134 types.put(converCase(rsmd.getColumnLabel(i + 1)), cn); 135 } 136 return types; 137 } 138 139 /** 140 * Get column db data types by table name 141 * @exception SQLException 142 * @return Map<String, Object> - column and db data type mapping 143 * @param tableName - Table name 144 * */ 145 public Map<String, Object> getColumnTypes(String tableName) throws SQLException { 146 ResultSet result = null; 147 PreparedStatement statement = null; 148 String sql = String.format("SELECT * FROM %s WHERE 1 = 0", tableName); 149 Map<String, Object> types = null; 150 statement = connection.prepareStatement(sql); 151 result = statement.executeQuery(); 152 final ResultSetMetaData rsmd = result.getMetaData(); 153 for (int i = 0; i < rsmd.getColumnCount(); i++) { 154 if (types == null) { 155 types = new ResultMap<String, Object>(); 156 } 157 types.put(converCase(rsmd.getColumnLabel(i + 1)), rsmd.getColumnTypeName(i + 1)); 158 } 159 return types; 160 } 161 162 /** 163 * Show column data type and java type mapping 164 * @exception SQLException 165 * @return List<Map<String, Object>> - Mapping list 166 * @param tableName - Table name 167 * */ 168 public List<Map<String, Object>> desc(String tableName) throws SQLException { 169 List<String> primaryKeys = getPrimaryKeys(tableName); 170 List<Map<String, Object>> results = new ArrayList<Map<String, Object>>(); 171 ResultSet result = null; 172 PreparedStatement statement = null; 173 String sql = String.format("SELECT * FROM %s WHERE 1 = 0", tableName); 174 statement = connection.prepareStatement(sql); 175 result = statement.executeQuery(); 176 final ResultSetMetaData rsmd = result.getMetaData(); 177 for (int i = 0; i < rsmd.getColumnCount(); i++) { 178 Map<String, Object> types = new ResultMap<String, Object>(); 179 int ci = i + 1; 180 boolean nullable = rsmd.isNullable(ci) == 1; 181 String name = converCase(rsmd.getColumnLabel(ci)); 182 String isPk = "UNKNOWN"; 183 if (primaryKeys != null) { 184 isPk = primaryKeys.contains(name) ? "Y" : "N"; 185 } 186 types.put("NAME", name); 187 types.put("PRIMARY_KEY", isPk); 188 types.put("DATA_TYPE", rsmd.getColumnTypeName(ci)); 189 types.put("JAVA_TYPE", rsmd.getColumnClassName(ci)); 190 types.put("PRECISION", rsmd.getPrecision(ci)); 191 types.put("ALLOW_NULLABLE", nullable ? "Y" : 'N'); 192 results.add(types); 193 } 194 return results; 195 } 196 197 /** 198 * Get primary Keys by table name 199 * @return List<String> - Primary Keys 200 * @param _tableName - Table name 201 * */ 202 public List<String> getPrimaryKeys(String _tableName) { 203 try { 204 List<String> pks = new ArrayList<String>(); 205 DatabaseMetaData meta = connection.getMetaData(); 206 ResultSet tables = meta.getTables(null, null, "%", new String[] { "TABLE" }); 207 while (tables.next()) { 208 String catalog = tables.getString("TABLE_CAT"); 209 String schema = tables.getString("TABLE_SCHEM"); 210 String tableName = tables.getString("TABLE_NAME"); 211 if (tableName.equalsIgnoreCase(_tableName)) { 212 ResultSet primaryKeys = meta.getPrimaryKeys(catalog, schema, tableName); 213 while (primaryKeys.next()) { 214 pks.add(primaryKeys.getString("COLUMN_NAME")); 215 } 216 break; 217 } 218 } 219 return pks; 220 } catch (Exception e) { 221 log.warn(e); 222 return null; 223 } 224 } 225 226 /** 227 * Get all tables 228 * @return List<Map<String, Object>> 229 * @exception SQLException 230 * */ 231 public List<Map<String, Object>> getAllTables() throws SQLException { 232 List<Map<String, Object>> tablesList = new ArrayList<Map<String, Object>>(); 233 DatabaseMetaData meta = connection.getMetaData(); 234 ResultSet tables = meta.getTables(null, null, "%", new String[] { "TABLE" }); 235 while (tables.next()) { 236 String catalog = tables.getString("TABLE_CAT"); 237 String schema = tables.getString("TABLE_SCHEM"); 238 String tableName = tables.getString("TABLE_NAME"); 239 Map<String, Object> t = new ResultMap<String, Object>(); 240 t.put("TABLE_SCHEMA", schema); 241 t.put("TABLE_NAME", tableName); 242 tablesList.add(t); 243 } 244 return tablesList; 245 } 246 247 /** 248 * Get all tables by schema 249 * @return List<Map<String, Object>> 250 * @exception SQLException 251 * */ 252 public List<Map<String, Object>> getAllTables(String _schema) throws SQLException { 253 List<Map<String, Object>> tablesList = new ArrayList<Map<String, Object>>(); 254 DatabaseMetaData meta = connection.getMetaData(); 255 ResultSet tables = meta.getTables(null, null, "%", new String[] { "TABLE" }); 256 while (tables.next()) { 257 String schema = tables.getString("TABLE_SCHEM"); 258 if (schema != null && schema.equalsIgnoreCase(_schema)) { 259 String catalog = tables.getString("TABLE_CAT"); 260 String tableName = tables.getString("TABLE_NAME"); 261 Map<String, Object> t = new ResultMap<String, Object>(); 262 t.put("TABLE_SCHEMA", schema); 263 t.put("TABLE_NAME", tableName); 264 tablesList.add(t); 265 } 266 } 267 return tablesList; 268 } 269 270 /** 271 * Query first record 272 * @exception SQLException 273 * @return Map<String,Object> - First result 274 * @param sql - Condition use format ':column_name' 275 * @param params 276 * */ 277 public Map<String, Object> first(String sql, Map<String, Object> params) throws SQLException { 278 List<Map<String, Object>> list = find(0, 1, sql, params); 279 if (list.size() > 0) { 280 return list.get(0); 281 } 282 return null; 283 } 284 285 /** 286 * Query first record 287 * @exception SQLException 288 * @return Map<String,Object> - First result 289 * @param sql 290 * */ 291 public Map<String, Object> first(String sql) throws SQLException { 292 return first(sql, Arrays.asList(new Object[] {})); 293 } 294 295 /** 296 * Query first record 297 * @exception SQLException 298 * @return Map<String,Object> - First result 299 * @param sql - Condition use format '?' 300 * @param params 301 * */ 302 public Map<String, Object> first(String sql, List<Object> params) throws SQLException { 303 List<Map<String, Object>> list = find(0, 1, sql, params); 304 if (list.size() > 0) { 305 return list.get(0); 306 } 307 return null; 308 } 309 310 /** 311 * Query all matched records 312 * @exception SQLException 313 * @return List<Map<String, Object>> 314 * @param sql 315 * @param params 316 * */ 317 public List<Map<String, Object>> find(String sql) throws SQLException { 318 return find(0, 0, sql, Arrays.asList(new Object[] {})); 319 } 320 321 /** 322 * Query all matched records 323 * @exception SQLException 324 * @return List<Map<String, Object>> 325 * @param cursorStart - JDBC result Cursor start index 326 * @param maxRows - JDBC result max rows (JDBC limited rows 50,000,000) 327 * @param sql 328 * @param params 329 * */ 330 public List<Map<String, Object>> find(int cursorStart, int maxRows, String sql) throws SQLException { 331 return find(cursorStart, maxRows, sql, Arrays.asList(new Object[] {})); 332 } 333 334 /** 335 * Query all matched records 336 * @exception SQLException 337 * @return List<Map<String, Object>> 338 * @param sql - Condition use format ':column_name' 339 * @param params 340 * */ 341 public List<Map<String, Object>> find(String sql, Map<String, Object> params) throws SQLException { 342 return find(0, 0, sql, params); 343 } 344 345 /** 346 * Query all matched records 347 * @exception SQLException 348 * @return List<Map<String, Object>> 349 * @param cursorStart - JDBC result Cursor start index 350 * @param maxRows - JDBC result max rows (JDBC limited rows 50,000,000) 351 * @param sql - Condition use format ':column_name' 352 * @param params 353 * */ 354 public List<Map<String, Object>> find(int cursorStart, int maxRows, String sql, Map<String, Object> params) 355 throws SQLException { 356 String csql = converSql(sql); 357 List<Object> cparams = converParams(sql, params); 358 return find(cursorStart, maxRows, csql, cparams); 359 } 360 361 /** 362 * Query all matched records 363 * @exception SQLException 364 * @return List<Map<String, Object>> 365 * @param cursorStart - JDBC result Cursor start index 366 * @param maxRows - JDBC result max rows (JDBC limited rows 50,000,000) 367 * @param sql - Condition use format '?' 368 * @param params 369 * */ 370 public List<Map<String, Object>> find(int cursorStart, int maxRows, String sql, List<Object> params) 371 throws SQLException { 372 long begin = System.currentTimeMillis(); 373 boolean allowedLog = writeSqlLog("find", begin, 374 String.format("%s [cursorStart=%s,maxRows=%s]", sql, cursorStart, maxRows), params); 375 376 PreparedStatement statement = null; 377 Map<String, Object> row = null; 378 ResultSet result = null; 379 final List<Map<String, Object>> rows = new ArrayList<Map<String, Object>>(); 380 try { 381 // ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE 382 // ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY 383 statement = connection.prepareStatement(sql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY); 384 if (params != null) { 385 int size = params.size(); 386 for (int i = 0; i < size; i++) { 387 int ci = i + 1; 388 Object param = params.get(i); 389 if (param == null) { 390 statement.setNull(ci, Types.VARCHAR); 391 } else { 392 statement.setObject(ci, param); 393 } 394 } 395 } 396 if (maxRows > 0) { 397 statement.setMaxRows(maxRows); 398 } 399 result = statement.executeQuery(); 400 result.absolute(cursorStart); 401 final ResultSetMetaData rsmd = result.getMetaData(); 402 final int c = rsmd.getColumnCount(); 403 while (result.next()) { 404 row = new ResultMap<String, Object>(); 405 for (int i = 0; i < c; i++) { 406 int ci = i + 1; 407 Object value = null; 408 Object originValue = result.getObject(ci); 409 if (originValue == null) { 410 value = originValue; 411 } else if (originValue instanceof Blob) { 412 Blob blobValue = (Blob) originValue; 413 InputStream is = null; 414 try { 415 is = blobValue.getBinaryStream(); 416 if(is != null) value = is.readAllBytes(); 417 } catch (IOException e) { 418 throw new SQLException(e.getMessage(), e); 419 } finally { 420 if (blobValue != null) { 421 try { 422 blobValue.free(); 423 } catch (SQLException e) { 424 throw e; 425 } 426 } 427 if (is != null) { 428 try { 429 is.close(); 430 } catch (IOException e) { 431 throw new SQLException(e.getMessage(), e); 432 } 433 } 434 } 435 } else if (originValue instanceof Clob) { 436 Clob clobValue = (Clob) originValue; 437 InputStream is = null; 438 try { 439 is = clobValue.getAsciiStream(); 440 if(is != null) value = is.readAllBytes(); 441 } catch (IOException e) { 442 throw new SQLException(e.getMessage(), e); 443 } finally { 444 if (clobValue != null) { 445 try { 446 clobValue.free(); 447 } catch (SQLException e) { 448 throw e; 449 } 450 } 451 if (is != null) { 452 try { 453 is.close(); 454 } catch (IOException e) { 455 throw new SQLException(e.getMessage(), e); 456 } 457 } 458 } 459 } else { 460 value = originValue; 461 } 462 row.put(converCase(rsmd.getColumnLabel(ci)), value); 463 } 464 rows.add(row); 465 } 466 if (allowedLog) { 467 writeSqlLog("rows", begin, "rows", rows.size()); 468 long spend = System.currentTimeMillis() - begin; 469 writeSqlLog("spend", begin, "spend", spend); 470 } 471 return rows; 472 } catch (SQLException e) { 473 if (allowedLog) 474 writeSqlLog("error", begin, "error", String.format("%s,%s", e.getErrorCode(), e.getMessage())); 475 476 throw e; 477 } finally { 478 if (result != null) 479 result.close(); 480 481 if (statement != null) 482 statement.close(); 483 } 484 } 485 486 /** 487 * Execute stored proc (and return boolean) 488 * @param sql - Query sql 489 * @exception SQLException 490 * @return boolean 491 * */ 492 public boolean callAndReturnBoolean(String sql) throws SQLException { 493 return callAndReturnBoolean(sql, Arrays.asList(new Object[] {})); 494 } 495 496 /** 497 * Execute stored proc (and return boolean) 498 * @param sql - Query sql, condition use format ':column_name' 499 * @param params 500 * @exception SQLException 501 * @return boolean 502 * */ 503 public boolean callAndReturnBoolean(String sql, Map<String, Object> params) throws SQLException { 504 String csql = converSql(sql); 505 List<Object> cparams = converParams(sql, params); 506 return callAndReturnBoolean(sql, params); 507 } 508 509 /** 510 * Execute stored proc (and return boolean) 511 * @param sql - Query sql,condition use format '?' 512 * @param params 513 * @exception SQLException 514 * @return boolean 515 * */ 516 public boolean callAndReturnBoolean(String sql, List<Object> params) throws SQLException { 517 518 if (!checkSqlAvailable(sql)) 519 return false; 520 521 long begin = System.currentTimeMillis(); 522 boolean allowedLog = writeSqlLog("call", begin, sql, params); 523 524 PreparedStatement statement = null; 525 try { 526 statement = connection.prepareCall("{" + sql + "}"); 527 if (params != null) { 528 int size = params.size(); 529 for (int i = 0; i < size; i++) { 530 Object param = params.get(i); 531 if (param == null) { 532 statement.setNull(i + 1, Types.VARCHAR); 533 } else { 534 statement.setObject(i + 1, param); 535 } 536 } 537 } 538 539 boolean returnResult = statement.execute(); 540 541 if (allowedLog) { 542 writeSqlLog("return", begin, "return", returnResult); 543 long spend = System.currentTimeMillis() - begin; 544 writeSqlLog("spend", begin, "spend", spend); 545 } 546 547 callAndReturnBooleanSync(connection, begin, sql, params, returnResult); 548 549 return returnResult; 550 } catch (SQLException e) { 551 if (allowedLog) 552 writeSqlLog("error", begin, "error", String.format("%s,%s", e.getErrorCode(), e.getMessage())); 553 554 throw e; 555 } finally { 556 if (statement != null) 557 statement.close(); 558 } 559 } 560 561 /** 562 * Execute stored proc (and return rows) 563 * @param sql - Query sql 564 * @exception SQLException 565 * @return int 566 * */ 567 public int callAndReturnRows(String sql) throws SQLException { 568 return callAndReturnRows(sql, Arrays.asList(new Object[] {})); 569 } 570 571 /** 572 * Execute stored proc (and return rows) 573 * @param sql - Query sql,condition use format ':column_name' 574 * @param params 575 * @exception SQLException 576 * @return int 577 * */ 578 public int callAndReturnRows(String sql, Map<String, Object> params) throws SQLException { 579 String csql = converSql(sql); 580 List<Object> cparams = converParams(sql, params); 581 return callAndReturnRows(sql, params); 582 } 583 584 /** 585 * Execute stored proc (and return rows) 586 * @param sql - Query sql,condition use format '?' 587 * @param params 588 * @exception SQLException 589 * @return int 590 * */ 591 public int callAndReturnRows(String sql, List<Object> params) throws SQLException { 592 593 if (!checkSqlAvailable(sql)) 594 return -1; 595 596 long begin = System.currentTimeMillis(); 597 boolean allowedLog = writeSqlLog("call", begin, sql, params); 598 599 PreparedStatement statement = null; 600 try { 601 statement = connection.prepareCall("{" + sql + "}"); 602 if (params != null) { 603 int size = params.size(); 604 for (int i = 0; i < size; i++) { 605 Object param = params.get(i); 606 if (param == null) { 607 statement.setNull(i + 1, Types.VARCHAR); 608 } else { 609 statement.setObject(i + 1, param); 610 } 611 } 612 } 613 int row = statement.executeUpdate(); 614 615 if (allowedLog) { 616 writeSqlLog("return", begin, "return", row); 617 long spend = System.currentTimeMillis() - begin; 618 writeSqlLog("spend", begin, "spend", spend); 619 } 620 621 callAndReturnRowsSync(connection, begin, sql, params, row); 622 623 return row; 624 } catch (SQLException e) { 625 if (allowedLog) 626 writeSqlLog("error", begin, "error", String.format("%s,%s", e.getErrorCode(), e.getMessage())); 627 628 throw e; 629 } finally { 630 if (statement != null) 631 statement.close(); 632 } 633 } 634 635 /** 636 * Execute stored proc (and return List) 637 * @param sql - Query sql 638 * @exception SQLException 639 * @return List<Map<String, Object>> 640 * */ 641 public List<Map<String, Object>> callAndReturnList(String sql) throws SQLException { 642 return callAndReturnList(0, 0, sql, Arrays.asList(new Object[] {})); 643 } 644 645 /** 646 * Execute stored proc (and return List) 647 * @param cursorStart - JDBC result Cursor start index 648 * @param maxRows - JDBC result max rows (JDBC limited rows 50,000,000) 649 * @param sql - Query sql 650 * @exception SQLException 651 * @return List<Map<String, Object>> 652 * */ 653 public List<Map<String, Object>> callAndReturnList(int cursorStart, int maxRows, String sql) throws SQLException { 654 return callAndReturnList(cursorStart, maxRows, sql, Arrays.asList(new Object[] {})); 655 } 656 657 /** 658 * Execute stored proc (and return List) 659 * @param cursorStart - JDBC result Cursor start index 660 * @param maxRows - JDBC result max rows (JDBC limited rows 50,000,000) 661 * @param sql - Query sql, condition use format ':column_name' 662 * @exception SQLException 663 * @return List<Map<String, Object>> 664 * */ 665 public List<Map<String, Object>> callAndReturnList(int cursorStart, int maxRows, String sql, 666 Map<String, Object> params) throws SQLException { 667 String csql = converSql(sql); 668 List<Object> cparams = converParams(sql, params); 669 return callAndReturnList(cursorStart, maxRows, sql, params); 670 } 671 672 /** 673 * Execute stored proc (and return List) 674 * @param cursorStart - JDBC result Cursor start index 675 * @param maxRows - JDBC result max rows (JDBC limited rows 50,000,000) 676 * @param sql - Query sql, condition use format '?' 677 * @exception SQLException 678 * @return List<Map<String, Object>> 679 * */ 680 public List<Map<String, Object>> callAndReturnList(int cursorStart, int maxRows, String sql, List<Object> params) 681 throws SQLException { 682 683 if (!checkSqlAvailable(sql)) 684 return null; 685 686 long begin = System.currentTimeMillis(); 687 boolean allowedLog = writeSqlLog("call", begin, 688 String.format("%s [cursorStart=%s,maxRows=%s]", sql, cursorStart, maxRows), params); 689 690 PreparedStatement statement = null; 691 Map<String, Object> row = null; 692 ResultSet result = null; 693 final List<Map<String, Object>> rows = new ArrayList<Map<String, Object>>(); 694 try { 695 statement = connection.prepareCall("{" + sql + "}"); 696 if (params != null) { 697 int size = params.size(); 698 for (int i = 0; i < size; i++) { 699 int ci = i + 1; 700 Object param = params.get(i); 701 if (param == null) { 702 statement.setNull(ci, Types.VARCHAR); 703 } else { 704 statement.setObject(ci, param); 705 } 706 } 707 } 708 if (maxRows > 0) { 709 statement.setMaxRows(maxRows); 710 } 711 result = statement.executeQuery(); 712 final ResultSetMetaData rsmd = result.getMetaData(); 713 final int c = rsmd.getColumnCount(); 714 int rowIndex = 0; 715 while (result.next()) { 716 if (rowIndex >= cursorStart) { 717 row = new ResultMap<String, Object>(); 718 for (int i = 0; i < c; i++) { 719 int ci = i + 1; 720 Object value = null; 721 Object originValue = result.getObject(ci); 722 if (originValue == null) { 723 value = originValue; 724 } else if (originValue instanceof Blob) { 725 Blob blobValue = (Blob) originValue; 726 InputStream is = null; 727 try { 728 is = blobValue.getBinaryStream(); 729 if(is != null) value = is.readAllBytes(); 730 } catch (IOException e) { 731 throw new SQLException(e.getMessage(), e); 732 } finally { 733 if (blobValue != null) { 734 try { 735 blobValue.free(); 736 } catch (SQLException e) { 737 throw e; 738 } 739 } 740 if (is != null) { 741 try { 742 is.close(); 743 } catch (IOException e) { 744 throw new SQLException(e.getMessage(), e); 745 } 746 } 747 } 748 } else if (originValue instanceof Clob) { 749 Clob clobValue = (Clob) originValue; 750 InputStream is = null; 751 try { 752 is = clobValue.getAsciiStream(); 753 if(is != null) value = is.readAllBytes(); 754 } catch (IOException e) { 755 throw new SQLException(e.getMessage(), e); 756 } finally { 757 if (clobValue != null) { 758 try { 759 clobValue.free(); 760 } catch (SQLException e) { 761 throw e; 762 } 763 } 764 if (is != null) { 765 try { 766 is.close(); 767 } catch (IOException e) { 768 throw new SQLException(e.getMessage(), e); 769 } 770 } 771 } 772 } else { 773 value = originValue; 774 } 775 row.put(converCase(rsmd.getColumnLabel(ci)), value); 776 } 777 rows.add(row); 778 } 779 rowIndex++; 780 } 781 782 if (allowedLog) { 783 writeSqlLog("rows", begin, "rows", rows); 784 long spend = System.currentTimeMillis() - begin; 785 writeSqlLog("spend", begin, "spend", spend); 786 } 787 788 callAndReturnListSync(connection, cursorStart, maxRows, sql, params); 789 790 return rows; 791 } catch (SQLException e) { 792 if (allowedLog) 793 writeSqlLog("error", begin, "error", String.format("%s,%s", e.getErrorCode(), e.getMessage())); 794 throw e; 795 } finally { 796 if (result != null) 797 result.close(); 798 799 if (statement != null) 800 statement.close(); 801 } 802 } 803 804 /** 805 * Execute sql 806 * @exception SQLException 807 * @param sql 808 * @return int 809 * */ 810 public int execute(String sql) throws SQLException { 811 return execute(sql, Arrays.asList(new Object[] {})); 812 } 813 814 /** 815 * Execute sql 816 * @exception SQLException 817 * @param sql - Condition use format ':column_name' 818 * @param params 819 * @return int 820 * */ 821 public int execute(String sql, Map<String, Object> params) throws SQLException { 822 String csql = converSql(sql); 823 List<Object> cparams = converParams(sql, params); 824 return execute(csql, cparams); 825 } 826 827 /** 828 * Execute sql 829 * @exception SQLException 830 * @param sql - Condition use format '?' 831 * @param params 832 * @return int 833 * */ 834 public int execute(String sql, List<Object> params) throws SQLException { 835 if (!checkSqlAvailable(sql)) 836 return -1; 837 838 long begin = System.currentTimeMillis(); 839 boolean allowedLog = writeSqlLog("execute", begin, sql, params); 840 841 PreparedStatement statement = null; 842 try { 843 statement = connection.prepareStatement(sql); 844 if (params != null) { 845 int size = params.size(); 846 for (int i = 0; i < size; i++) { 847 Object param = params.get(i); 848 if (param == null) { 849 statement.setNull(i + 1, Types.VARCHAR); 850 } else { 851 statement.setObject(i + 1, param); 852 } 853 } 854 } 855 int row = statement.executeUpdate(); 856 857 if (allowedLog) { 858 writeSqlLog("return", begin, "return", row); 859 long spend = System.currentTimeMillis() - begin; 860 writeSqlLog("spend", begin, "spend", spend); 861 } 862 863 executeSync(connection, begin, sql, params, row); 864 865 return row; 866 } catch (SQLException e) { 867 if (allowedLog) 868 writeSqlLog("error", begin, "error", String.format("%s,%s", e.getErrorCode(), e.getMessage())); 869 throw e; 870 } finally { 871 if (statement != null) 872 statement.close(); 873 } 874 } 875 876 /** 877 * Execute batch 878 * @exception SQLException 879 * @param sql - Condition use format ':column_name' 880 * @param records 881 * @return int - Return rows 882 * */ 883 public int executeBatch(String sql, List<Map<String, Object>> records) throws SQLException { 884 String csql = converSql(sql); 885 List<List<Object>> crecords = new ArrayList<List<Object>>(); 886 for (Map<String, Object> record : records) { 887 List<Object> crecord = converParams(sql, record); 888 crecords.add(crecord); 889 } 890 return executeBatchList(csql, crecords); 891 } 892 893 /** 894 * Execute batch 895 * @exception SQLException 896 * @param sql - Condition use format '?' 897 * @param records 898 * @return int - Return rows 899 * */ 900 public int executeBatchList(String sql, List<List<Object>> records) throws SQLException { 901 boolean allowedLog = false; 902 if (!checkSqlAvailable(sql)) 903 return -1; 904 905 long begin = System.currentTimeMillis(); 906 if (records != null) { 907 allowedLog = writeSqlLog("batch", begin, sql, String.format("[batchSize=%s]", records.size())); 908 } 909 910 boolean first = true; 911 PreparedStatement statement = null; 912 try { 913 for (List<Object> params : records) { 914 if (first) { 915 statement = connection.prepareStatement(sql); 916 first = false; 917 } 918 919 int size = params.size(); 920 for (int i = 0; i < size; i++) { 921 Object param = params.get(i); 922 if (param == null) { 923 statement.setNull(i + 1, Types.VARCHAR); 924 } else { 925 statement.setObject(i + 1, param); 926 } 927 } 928 929 statement.addBatch(); 930 } 931 932 int sumRow = 0; 933 if (records.size() > 0) { 934 int[] rows = statement.executeBatch(); 935 936 for (int r : rows) { 937 sumRow += r; 938 } 939 940 if (allowedLog) { 941 long spend = System.currentTimeMillis() - begin; 942 writeSqlLog("return", begin, "return", sumRow); 943 writeSqlLog("spend", begin, "spend", spend); 944 } 945 946 executeBatchListSync(connection, begin, sql, records, sumRow); 947 } 948 949 return sumRow; 950 } catch (SQLException e) { 951 if (allowedLog) 952 writeSqlLog("error", begin, "error", String.format("%s,%s", e.getErrorCode(), e.getMessage())); 953 954 throw e; 955 } finally { 956 if (statement != null) 957 statement.close(); 958 } 959 } 960 961 /** 962 * Check connection is closed 963 * @return boolean 964 * */ 965 public boolean isClosed() { 966 try { 967 if(closed) return closed; 968 969 return (connection == null || connection.isClosed()); 970 } catch (Exception e) { 971 log.warn(e); 972 return true; 973 } 974 } 975 976 /** 977 * Abort connection 978 * @exception SQLException 979 * */ 980 public void abort() throws SQLException { 981 writeSqlLog("aborting", 0, "aborting", ""); 982 if (connection != null) { 983 try { 984 if (connection instanceof DriverConnection) { 985 abortSyncConnection(connection); 986 connection.abort(null); 987 } else { 988 connection.abort(Executors.newFixedThreadPool(1)); 989 } 990 closed = true; 991 } catch (SQLException e) { 992 writeSqlLog("error", 0, "error", String.format("%s,%s", e.getErrorCode(), e.getMessage())); 993 throw e; 994 } 995 } 996 writeSqlLog("aborted", 0, "aborted", ""); 997 } 998 999 /** 1000 * Close connection 1001 * @exception SQLException 1002 * */ 1003 public void close() throws SQLException { 1004 writeSqlLog("closing", 0, "closing", ""); 1005 if (connection != null) { 1006 try { 1007 closeSyncConnection(connection); 1008 connection.close(); 1009 closed = true; 1010 } catch (SQLException e) { 1011 writeSqlLog("error", 0, "error", String.format("%s,%s", e.getErrorCode(), e.getMessage())); 1012 throw e; 1013 } 1014 } 1015 writeSqlLog("closed", 0, "closed", ""); 1016 } 1017 1018 /** 1019 * Commit connection 1020 * @exception SQLException 1021 * */ 1022 public void commit() throws SQLException { 1023 writeSqlLog("committing", 0, "committing", ""); 1024 if (connection != null) { 1025 try { 1026 commitSyncConnection(connection); 1027 connection.commit(); 1028 } catch (SQLException e) { 1029 writeSqlLog("error", 0, "", String.format("%s,%s", e.getErrorCode(), e.getMessage())); 1030 throw e; 1031 } 1032 } 1033 writeSqlLog("committed", 0, "committed", ""); 1034 } 1035 1036 /** 1037 * Rollback connection 1038 * @exception SQLException 1039 * */ 1040 public void rollback() throws SQLException { 1041 writeSqlLog("rollbacking", 0, "rollbacking", ""); 1042 if (connection != null) { 1043 try { 1044 rollbackSyncConnection(connection); 1045 connection.rollback(); 1046 } catch (SQLException e) { 1047 writeSqlLog("error", 0, "error", String.format("%s,%s", e.getErrorCode(), e.getMessage())); 1048 throw e; 1049 } 1050 } 1051 writeSqlLog("rollbacked", 0, "rollbacked", ""); 1052 } 1053 1054 /** 1055 * Get origin connection hash code 1056 * @return Integer - hash code 1057 * */ 1058 private Integer getOriginConnectionHashCode() { 1059 if (connection instanceof DriverConnection) { 1060 return ((DriverConnection) connection).getOriginConnectionHashCode(); 1061 } else { 1062 return connection.hashCode(); 1063 } 1064 } 1065 1066 /** 1067 * Get data source thread name 1068 * @return String - DataSource thread name 1069 * */ 1070 private String getDataSourceName() { 1071 if (connection instanceof DriverConnection) { 1072 return ((DriverConnection) connection).getDriverDataSource().getName(); 1073 } else { 1074 return null; 1075 } 1076 } 1077 1078 /** 1079 * Conver sql from ':column_name' to '?' 1080 * @return String 1081 * */ 1082 protected String converSql(String sql) { 1083 return sql.replaceAll(":[a-zA-Z0-9_]+", "?"); 1084 } 1085 1086 /** 1087 * Conver param from map to list 1088 * @param sql 1089 * @param map 1090 * @return List<Object> 1091 * */ 1092 protected List<Object> converParams(String sql, Map<String, Object> map) { 1093 List<String> paramKeys = new ArrayList<String>(); 1094 Pattern pattern = Pattern.compile(":[a-zA-Z0-9_]+"); 1095 Matcher matcher = pattern.matcher(sql); 1096 while (matcher.find()) { 1097 String key = matcher.group().replaceFirst(":", ""); 1098 paramKeys.add(key); 1099 } 1100 1101 List<Object> params = new ArrayList<Object>(); 1102 for (String pk : paramKeys) { 1103 Object pv = map.get(pk); 1104 if (pv == null) { 1105 pv = map.get(pk.toLowerCase()); 1106 } 1107 if (pv == null) { 1108 pv = map.get(pk.toUpperCase()); 1109 } 1110 if (pv instanceof java.util.Date) { 1111 java.util.Date utilDate = (java.util.Date) pv; 1112 java.sql.Timestamp sqlDate = new java.sql.Timestamp(utilDate.getTime()); 1113 params.add(sqlDate); 1114 } else { 1115 params.add(pv); 1116 } 1117 } 1118 1119 return params; 1120 } 1121 1122 /** 1123 * Covner to upper or lower 1124 * @param s - Column name or table name 1125 * @return String 1126 * */ 1127 protected String converCase(String s) { 1128 if (COLUMN_NAME_CASE_MODE.equals(COLUMN_NAME_CASE_UPPER)) { 1129 return s.toUpperCase(); 1130 } else if (COLUMN_NAME_CASE_MODE.equals(COLUMN_NAME_CASE_LOWER)) { 1131 return s.toLowerCase(); 1132 } else { 1133 return s; 1134 } 1135 } 1136 1137 /** 1138 * Check available sql to write to log 1139 * @return boolean 1140 * @param connection - Connection 1141 * @param sql 1142 * */ 1143 protected static boolean checkSqlLogAvailable(Connection connection, String sql) { 1144 if (connection instanceof DriverConnection) { 1145 DriverConnection dc = (DriverConnection) connection; 1146 DriverDataSource dds = dc.getDriverDataSource(); 1147 ConfigProperties configProperties = dds.getConfigProperties(); 1148 List<String> sqlAllowed = configProperties.getArray("SqlLogAllowed"); 1149 List<String> sqlIgnored = configProperties.getArray("SqlLogIgnored"); 1150 return checkSqlAvailable(sql, sqlAllowed, sqlIgnored); 1151 } 1152 return false; 1153 } 1154 1155 /** 1156 * Check available sql to execute 1157 * @return boolean 1158 * @param sql 1159 * */ 1160 protected boolean checkSqlAvailable(String sql) { 1161 return checkSqlAvailable(connection, sql); 1162 } 1163 1164 /** 1165 * Check available sql to execute 1166 * @return boolean 1167 * @param connection 1168 * @param sql 1169 * */ 1170 protected static boolean checkSqlAvailable(Connection connection, String sql) { 1171 if (connection instanceof DriverConnection) { 1172 DriverConnection dc = (DriverConnection) connection; 1173 DriverDataSource dds = dc.getDriverDataSource(); 1174 ConfigProperties configProperties = dds.getConfigProperties(); 1175 List<String> sqlAllowed = configProperties.getArray("SqlExecuteAllowed"); 1176 List<String> sqlIgnored = configProperties.getArray("SqlExecuteIgnored"); 1177 return checkSqlAvailable(sql, sqlAllowed, sqlIgnored); 1178 } 1179 return true; 1180 } 1181 1182 /** 1183 * Check available sql to execute 1184 * @return boolean 1185 * @param sql 1186 * @param sqlAllowed - From DataSources.properties 1187 * @param sqlIgnored - From DataSources.properties 1188 * */ 1189 private static boolean checkSqlAvailable(String sql, List<String> sqlAllowed, List<String> sqlIgnored) { 1190 boolean matchedAllowed = true; 1191 boolean matchedIgnored = false; 1192 if (sqlAllowed != null) { 1193 for (String regex : sqlAllowed) { 1194 if (!CommonTools.isBlank(regex)) { 1195 matchedAllowed = sql.matches(regex); 1196 1197 if (matchedAllowed) 1198 break; 1199 } 1200 } 1201 } 1202 if (matchedAllowed && sqlIgnored != null) { 1203 for (String regex : sqlIgnored) { 1204 if (!CommonTools.isBlank(regex)) { 1205 matchedIgnored = sql.matches(regex); 1206 1207 if (matchedIgnored) 1208 break; 1209 } 1210 } 1211 } 1212 1213 boolean b = matchedAllowed && !matchedIgnored; 1214 if (!b) { 1215 LoggerFactory.getLogger(DriverExecutor.class).debug("Not available - '{}'", sql); 1216 } 1217 return b; 1218 } 1219 1220 /** 1221 * Write sql log 1222 * @return boolean 1223 * @param type 1224 * @param seq 1225 * @param sql 1226 * @param params 1227 * */ 1228 protected synchronized boolean writeSqlLog(String type, long seq, String sql, Object params) { 1229 return writeSqlLog(this.hashCode(),connection, type, seq, sql, params); 1230 } 1231 1232 /** 1233 * Write sql log 1234 * @return boolean 1235 * @param connection 1236 * @param type 1237 * @param seq 1238 * @param sql 1239 * @param params 1240 * */ 1241 protected synchronized static boolean writeSqlLog(int deHashCode,Connection connection, String type, long seq, String sql, 1242 Object params) { 1243 if (connection instanceof DriverConnection) { 1244 if (!checkSqlLogAvailable(connection, sql)) 1245 return false; 1246 1247 final String header = String.format( 1248 "LOG_DATE,LOG_HOUR,LOG_MI,LOG_SEC,LOG_MS,LOG_HOST,LOG_THREAD,LOG_DS,LOG_CONN_ID,LOG_EXEC_ID,LOG_TYPE,LOG_SEQ,LOG_SQL,LOG_PARAMS%s", 1249 System.lineSeparator()); 1250 final DateFormat df = new SimpleDateFormat("yyyyMMdd"); 1251 final DateFormat dtf = new SimpleDateFormat("yyyyMMdd,HH,mm,ss,SSS"); 1252 DriverConnection dc = (DriverConnection) connection; 1253 Integer connHashCode = dc.getOriginConnectionHashCode(); 1254 DriverDataSource dds = dc.getDriverDataSource(); 1255 ConfigProperties configProperties = dds.getConfigProperties(); 1256 1257 boolean logEnable = configProperties.getBoolean("SqlLogEnable", false); 1258 long overspend = configProperties.getMilliSeconds("SqlLogOverspend",0L); 1259 1260 if (!logEnable) 1261 return false; 1262 1263 final String defaultLogFolderPath = String.format("%s/SqlLog/", 1264 CommonTools.getJarPath(DriverExecutor.class)); 1265 final String logFolderPath = configProperties.getString("SqlLogFolder", defaultLogFolderPath); 1266 final long maxFileSize = configProperties.getFileSize("SqlLogMaxFileSize", 1024 * 1024 * 10L); 1267 final int archiveDays = configProperties.getInteger("SqlLogArchiveDays", 31); 1268 final int logParamMaxLength = configProperties.getInteger("SqlLogParamMaxLength", 20); 1269 1270 if (sqlLogCacheArray == null) { 1271 1272 sqlLogCacheArray = new CacheArray(); 1273 long sqlLogFilterTimer = configProperties.getLong("SqlLogTimer", 100L); 1274 sqlLogCacheArray.filter(new CacheArrayFilter(sqlLogFilterTimer) { 1275 @Override 1276 public void execute(Integer index, Object o) { 1277 try { 1278 String msg = (String) o; 1279 String dateStr = df.format(new Timestamp(System.currentTimeMillis())); 1280 File sqlLogFile = new File(String.format("%s/%s.csv", logFolderPath, dateStr)); 1281 File sqlLogFolder = new File(sqlLogFile.getParent()); 1282 1283 if (sqlLogFolder.exists()) { 1284 if (!sqlLogFolder.canWrite()) 1285 throw new IOException(String.format("Can not write to log folder '%s'", 1286 sqlLogFolder.getAbsolutePath())); 1287 } else { 1288 sqlLogFolder.mkdirs(); 1289 } 1290 1291 if (sqlLogFile.exists()) { 1292 if (!sqlLogFile.canWrite()) 1293 throw new IOException(String.format("Can not write to log file '%s'", 1294 sqlLogFile.getAbsolutePath())); 1295 } 1296 1297 int suffixIndex = sqlLogFile.getName().lastIndexOf("."); 1298 String logFileNamePrefix = sqlLogFile.getName().substring(0, suffixIndex); 1299 long logSize = FileTools.size(sqlLogFile); 1300 if (!sqlLogFile.exists()) { 1301 FileTools.write(sqlLogFile, header, false); 1302 } 1303 if (logSize < maxFileSize) { 1304 FileTools.write(sqlLogFile, msg, true); 1305 } else { 1306 int logIndex = getLogFileIndex(configProperties, sqlLogFolder, logFileNamePrefix, 1307 "csv"); 1308 if (logIndex == 0) { 1309 FileTools.write(sqlLogFile, String.format("%s%s", header, msg), false); 1310 } else { 1311 backupLog(sqlLogFolder, sqlLogFile, logFileNamePrefix, logIndex, header, msg); 1312 } 1313 } 1314 archiveLog(archiveDays, sqlLogFolder); 1315 } catch (Exception e) { 1316 LoggerFactory.getLogger(DriverExecutor.class).warn(e.getMessage(), e); 1317 } 1318 } 1319 }); 1320 } 1321 1322 if (logEnable) { 1323 String hostname = CommonTools.getHostname(); 1324 String dateTimeStr = dtf.format(new Timestamp(System.currentTimeMillis())); 1325 boolean isOverspend = false; 1326 boolean isSpendSeq = sql.equals("spend"); 1327 if(isSpendSeq && params != null){ 1328 long spendValue = Long.parseLong(params + ""); 1329 isOverspend = (spendValue >= overspend); 1330 } 1331 1332 String threadId = Thread.currentThread().getName() + "-" + Thread.currentThread().getId(); 1333 String msg = String.format("%s,%s,%s,%s,%s,%s,%s,%s,\"%s\",\"%s\"%s", dateTimeStr, hostname, 1334 threadId, dds.getName(), connHashCode,deHashCode, type, seq, 1335 replaceToSigleLine(sql), handleParams(logParamMaxLength, params), System.lineSeparator()); 1336 1337 String key = String.format("%s_%s",deHashCode,connHashCode); 1338 if(seq <= 0 || overspend <= 0){ 1339 sqlLogCacheArray.add(msg); 1340 SQL_LOG_MSG_MAPPING.remove(key); 1341 SQL_LOG_OVERSPEND_MAPPING.remove(key); 1342 } 1343 if(seq > 0 && overspend > 0){ 1344 SQL_LOG_OVERSPEND_MAPPING.put(key,isOverspend); 1345 String existsMsg = SQL_LOG_MSG_MAPPING.get(key); 1346 existsMsg = existsMsg == null ? msg : (existsMsg + msg); 1347 SQL_LOG_MSG_MAPPING.put(key, existsMsg); 1348 1349 boolean seqOverspend = SQL_LOG_OVERSPEND_MAPPING.get(key); 1350 if(isSpendSeq && seqOverspend){ 1351 sqlLogCacheArray.add(SQL_LOG_MSG_MAPPING.get(key) + ""); 1352 SQL_LOG_MSG_MAPPING.remove(key); 1353 SQL_LOG_OVERSPEND_MAPPING.remove(key); 1354 } 1355 if(isSpendSeq && !seqOverspend){ 1356 SQL_LOG_MSG_MAPPING.remove(key); 1357 SQL_LOG_OVERSPEND_MAPPING.remove(key); 1358 } 1359 } 1360 return true; 1361 } 1362 1363 } 1364 return false; 1365 } 1366 1367 /** 1368 * Backup log file 1369 * */ 1370 private static void backupLog(File logFolder, File logFile, String logFileNamePrefix, int logIndex, String header, 1371 String msg) throws Exception { 1372 String backupLogFileName = String.format("%s/%s.%s.csv", logFolder.getAbsolutePath(), logFileNamePrefix, 1373 logIndex); 1374 Path source = Paths.get(logFile.getAbsolutePath()); 1375 Path target = Paths.get(backupLogFileName); 1376 Files.move(source, target, StandardCopyOption.REPLACE_EXISTING); 1377 FileTools.write(logFile, String.format("%s%s", header, msg), false); 1378 } 1379 1380 /** 1381 * Get log file index 1382 * For backup log file use 1383 * */ 1384 private static int getLogFileIndex(ConfigProperties configProperties, File folder, String prefix, String suffix) { 1385 Integer maxBackupIndex = configProperties.getInteger("SqlLogMaxBackupIndex", 10); 1386 for (int i = 1; i <= maxBackupIndex; i++) { 1387 String logFileName = String.format("%s/%s.%s.%s", folder.getAbsolutePath(), prefix, i, suffix); 1388 File logFile = new File(logFileName); 1389 if (!logFile.exists()) 1390 return i; 1391 } 1392 return 0; 1393 } 1394 1395 /** 1396 * Archive Log 1397 * */ 1398 private static void archiveLog(int archiveDays, File sqlLogFolder) { 1399 if (archiveDays > 0) { 1400 try { 1401 long archiveDaysMs = new Date().getTime() - (archiveDays * 24 * 3600000L); 1402 deleteFilesOlderThan(sqlLogFolder, archiveDaysMs); 1403 } catch (Exception e) { 1404 LoggerFactory.getLogger(DriverExecutor.class).warn(e); 1405 } 1406 } 1407 } 1408 1409 /** 1410 * Delete old archive logs 1411 * */ 1412 private static void deleteFilesOlderThan(File directory, long archiveDaysMs) throws IOException { 1413 if (directory.isDirectory()) { 1414 File[] files = directory.listFiles(); 1415 if (files != null) { 1416 for (File file : files) { 1417 if (file.isFile()) { 1418 boolean isLogFile = file.getName().toLowerCase().endsWith(".csv"); 1419 if (isLogFile) { 1420 boolean canWrite = file.canWrite(); 1421 if (canWrite) { 1422 long lastModified = file.lastModified(); 1423 if (lastModified < archiveDaysMs) { 1424 Files.deleteIfExists(Paths.get(file.toURI())); 1425 } 1426 } 1427 } 1428 } 1429 } 1430 } 1431 } 1432 } 1433 1434 /** 1435 * Replace to sigle line 1436 * For write csv log 1437 * */ 1438 private static String replaceToSigleLine(String msg) { 1439 return CodeEscape.escapeToSingleLineForCsv(msg); 1440 } 1441 1442 /** 1443 * Hahdle Params 1444 * For write csv log 1445 * */ 1446 private static String handleParams(int paramMaxLength, Object params) { 1447 1448 if (params == null) 1449 return "null"; 1450 1451 StringBuffer paramSbf = new StringBuffer(""); 1452 if (params instanceof List) { 1453 List<Object> listParams = (List<Object>) params; 1454 int size = listParams.size(); 1455 for (int i = 0; i < size; i++) { 1456 Object param = listParams.get(i); 1457 String str = param + ""; 1458 if (str.length() > paramMaxLength) { 1459 paramSbf.append(str.substring(0, paramMaxLength) + "..."); 1460 } else { 1461 paramSbf.append(str); 1462 } 1463 if (i < size - 1) { 1464 paramSbf.append(";"); 1465 } 1466 } 1467 } else { 1468 paramSbf.append(params); 1469 } 1470 return replaceToSigleLine(paramSbf.toString()); 1471 } 1472 1473 /**For Sync DataSource**/ 1474 1475 /** 1476 * For database replication 1477 * */ 1478 protected static void callAndReturnBooleanSync(Connection masterConn, long seq, String sql, List<Object> params, 1479 boolean returnResult) throws SQLException { 1480 if (masterConn instanceof DriverConnection) { 1481 openSyncConnection(masterConn); 1482 1483 DriverConnection masterDriverConn = (DriverConnection) masterConn; 1484 Integer masterConnHashCode = masterDriverConn.getOriginConnectionHashCode(); 1485 ConfigProperties ddscp = masterDriverConn.getDriverDataSource().getConfigProperties(); 1486 boolean connCheck = true; 1487 boolean returnCheck = true; 1488 if (ddscp != null) { 1489 connCheck = ddscp.getBoolean("SyncConnectionCheck", true); 1490 returnCheck = ddscp.getBoolean("SyncReturnCheck", true); 1491 } 1492 List<DriverExecutor> deList = SYNC_EXECUTOR_MARK.get(masterConnHashCode); 1493 if (deList != null) { 1494 for (DriverExecutor de : deList) { 1495 try { 1496 boolean resultSync = de.callAndReturnBoolean(sql, params); 1497 if (returnCheck) { 1498 if (resultSync != returnResult) { 1499 writeSqlLog(de.hashCode(),masterDriverConn, "diffed", seq, "diffed", String.format("%s-%s", 1500 de.getDataSourceName(), de.getOriginConnectionHashCode())); 1501 int errorCode = 99906; 1502 String errorMsg = String.format("The returned results are inconsistent '%s-%s'.", 1503 de.getDataSourceName(), de.getOriginConnectionHashCode()); 1504 throw new SQLException(errorMsg) { 1505 @Override 1506 public int getErrorCode() { 1507 return errorCode; 1508 } 1509 }; 1510 } 1511 } 1512 } catch (SQLException e) { 1513 LoggerFactory.getLogger(DriverExecutor.class) 1514 .warn(String.format("ERROR CODE: (%s) %s", e.getErrorCode(), e.getMessage())); 1515 if (connCheck) 1516 throw e; 1517 } 1518 } 1519 } 1520 } 1521 } 1522 1523 /** 1524 * For database replication 1525 * */ 1526 protected static void callAndReturnListSync(Connection masterConn, int cursorStart, int maxRows, String sql, 1527 List<Object> params) throws SQLException { 1528 if (masterConn instanceof DriverConnection) { 1529 openSyncConnection(masterConn); 1530 1531 DriverConnection masterDriverConn = (DriverConnection) masterConn; 1532 Integer masterConnHashCode = masterDriverConn.getOriginConnectionHashCode(); 1533 1534 List<DriverExecutor> deList = SYNC_EXECUTOR_MARK.get(masterConnHashCode); 1535 if (deList != null) { 1536 for (DriverExecutor de : deList) { 1537 try { 1538 de.callAndReturnBoolean(sql, params); 1539 } catch (SQLException e) { 1540 LoggerFactory.getLogger(DriverExecutor.class) 1541 .warn(String.format("ERROR CODE: (%s) %s", e.getErrorCode(), e.getMessage())); 1542 ConfigProperties ddscp = masterDriverConn.getDriverDataSource().getConfigProperties(); 1543 boolean connCheck = true; 1544 if (ddscp != null) { 1545 connCheck = ddscp.getBoolean("SyncConnectionCheck", true); 1546 } 1547 if (connCheck) 1548 throw e; 1549 } 1550 } 1551 } 1552 } 1553 } 1554 1555 /** 1556 * For database replication 1557 * */ 1558 protected static void callAndReturnRowsSync(Connection masterConn, long seq, String sql, List<Object> params, 1559 int returnRows) throws SQLException { 1560 if (masterConn instanceof DriverConnection) { 1561 openSyncConnection(masterConn); 1562 1563 DriverConnection masterDriverConn = (DriverConnection) masterConn; 1564 Integer masterConnHashCode = masterDriverConn.getOriginConnectionHashCode(); 1565 ConfigProperties ddscp = masterDriverConn.getDriverDataSource().getConfigProperties(); 1566 boolean connCheck = true; 1567 boolean returnCheck = true; 1568 if (ddscp != null) { 1569 connCheck = ddscp.getBoolean("SyncConnectionCheck", true); 1570 returnCheck = ddscp.getBoolean("SyncReturnCheck", true); 1571 } 1572 List<DriverExecutor> deList = SYNC_EXECUTOR_MARK.get(masterConnHashCode); 1573 if (deList != null) { 1574 for (DriverExecutor de : deList) { 1575 try { 1576 int rowSync = de.callAndReturnRows(sql, params); 1577 if (returnCheck) { 1578 if (rowSync != returnRows) { 1579 writeSqlLog(de.hashCode(),masterDriverConn, "diffed", seq, "diffed", String.format("%s-%s", 1580 de.getDataSourceName(), de.getOriginConnectionHashCode())); 1581 int errorCode = 99906; 1582 String errorMsg = String.format("The returned results are inconsistent '%s-%s'.", 1583 de.getDataSourceName(), de.getOriginConnectionHashCode()); 1584 throw new SQLException(errorMsg) { 1585 @Override 1586 public int getErrorCode() { 1587 return errorCode; 1588 } 1589 }; 1590 } 1591 } 1592 } catch (SQLException e) { 1593 LoggerFactory.getLogger(DriverExecutor.class) 1594 .warn(String.format("ERROR CODE: (%s) %s", e.getErrorCode(), e.getMessage())); 1595 if (connCheck) 1596 throw e; 1597 } 1598 } 1599 } 1600 } 1601 } 1602 1603 /** 1604 * For database replication 1605 * */ 1606 protected static void executeBatchListSync(Connection masterConn, long seq, String sql, List<List<Object>> records, 1607 int returnRows) throws SQLException { 1608 if (masterConn instanceof DriverConnection) { 1609 openSyncConnection(masterConn); 1610 1611 DriverConnection masterDriverConn = (DriverConnection) masterConn; 1612 Integer masterConnHashCode = masterDriverConn.getOriginConnectionHashCode(); 1613 ConfigProperties ddscp = masterDriverConn.getDriverDataSource().getConfigProperties(); 1614 boolean connCheck = true; 1615 boolean returnCheck = true; 1616 if (ddscp != null) { 1617 connCheck = ddscp.getBoolean("SyncConnectionCheck", true); 1618 returnCheck = ddscp.getBoolean("SyncReturnCheck", true); 1619 } 1620 List<DriverExecutor> deList = SYNC_EXECUTOR_MARK.get(masterConnHashCode); 1621 if (deList != null) { 1622 for (DriverExecutor de : deList) { 1623 try { 1624 int rowSync = de.executeBatchList(sql, records); 1625 if (returnCheck) { 1626 if (rowSync != returnRows) { 1627 writeSqlLog(de.hashCode(),masterDriverConn, "diffed", seq, "diffed", String.format("%s-%s", 1628 de.getDataSourceName(), de.getOriginConnectionHashCode())); 1629 int errorCode = 99906; 1630 String errorMsg = String.format("The returned results are inconsistent '%s-%s'.", 1631 de.getDataSourceName(), de.getOriginConnectionHashCode()); 1632 throw new SQLException(errorMsg) { 1633 @Override 1634 public int getErrorCode() { 1635 return errorCode; 1636 } 1637 }; 1638 } 1639 } 1640 } catch (SQLException e) { 1641 LoggerFactory.getLogger(DriverExecutor.class) 1642 .warn(String.format("ERROR CODE: (%s) %s", e.getErrorCode(), e.getMessage())); 1643 if (connCheck) 1644 throw e; 1645 } 1646 } 1647 } 1648 } 1649 } 1650 1651 /** 1652 * For database replication 1653 * */ 1654 protected static void executeSync(Connection masterConn, long seq, String sql, List<Object> params, int returnRows) 1655 throws SQLException { 1656 if (masterConn instanceof DriverConnection) { 1657 openSyncConnection(masterConn); 1658 1659 DriverConnection masterDriverConn = (DriverConnection) masterConn; 1660 Integer masterConnHashCode = masterDriverConn.getOriginConnectionHashCode(); 1661 ConfigProperties ddscp = masterDriverConn.getDriverDataSource().getConfigProperties(); 1662 boolean connCheck = true; 1663 boolean returnCheck = true; 1664 if (ddscp != null) { 1665 connCheck = ddscp.getBoolean("SyncConnectionCheck", true); 1666 returnCheck = ddscp.getBoolean("SyncReturnCheck", true); 1667 } 1668 List<DriverExecutor> deList = SYNC_EXECUTOR_MARK.get(masterConnHashCode); 1669 if (deList != null) { 1670 for (DriverExecutor de : deList) { 1671 try { 1672 int rowSync = de.execute(sql, params); 1673 if (returnCheck) { 1674 if (rowSync != returnRows) { 1675 writeSqlLog(de.hashCode(),masterDriverConn, "diffed", seq, "diffed", String.format("%s-%s", 1676 de.getDataSourceName(), de.getOriginConnectionHashCode())); 1677 int errorCode = 99906; 1678 String errorMsg = String.format("The returned results are inconsistent '%s-%s'.", 1679 de.getDataSourceName(), de.getOriginConnectionHashCode()); 1680 throw new SQLException(errorMsg) { 1681 @Override 1682 public int getErrorCode() { 1683 return errorCode; 1684 } 1685 }; 1686 } 1687 } 1688 } catch (SQLException e) { 1689 LoggerFactory.getLogger(DriverExecutor.class) 1690 .warn(String.format("ERROR CODE: (%s) %s", e.getErrorCode(), e.getMessage())); 1691 if (connCheck) 1692 throw e; 1693 } 1694 } 1695 } 1696 } 1697 } 1698 1699 /** 1700 * For database replication 1701 * */ 1702 protected synchronized static boolean openSyncConnection(Connection masterConn) throws SQLException { 1703 if (masterConn instanceof DriverConnection) { 1704 1705 DriverConnection masterDriverConn = (DriverConnection) masterConn; 1706 Integer masterConnHashCode = masterDriverConn.getOriginConnectionHashCode(); 1707 1708 boolean isExist = SYNC_EXECUTOR_MARK.containsKey(masterConnHashCode); 1709 1710 if (isExist) { 1711 return false; 1712 } else { 1713 SYNC_EXECUTOR_MARK.put(masterConnHashCode, new ArrayList<DriverExecutor>()); 1714 } 1715 1716 DriverDataSource dds = masterDriverConn.getDriverDataSource(); 1717 if (dds != null) { 1718 List<DriverDataSource> sdsl = dds.getSyncDataSourceList(dds.getName()); 1719 if (sdsl != null) { 1720 1721 if (sdsl.isEmpty()) 1722 return false; 1723 1724 for (DriverDataSource sds : sdsl) { 1725 1726 Integer sdsHashCode = sds.hashCode(); 1727 if (!SYNC_CONN_ERROR_TIME.containsKey(sdsHashCode)) { 1728 SYNC_CONN_ERROR_TIME.put(sdsHashCode, 0L); 1729 } 1730 1731 Long syncConnErrorTime = SYNC_CONN_ERROR_TIME.get(sdsHashCode); 1732 if (syncConnErrorTime > 0) { 1733 ConfigProperties ddscp = dds.getConfigProperties(); 1734 long connCheckMs = 10000L; 1735 if (ddscp != null) { 1736 connCheckMs = ddscp.getLong("SyncConnectionCheckTime", 10000L); 1737 } 1738 boolean isSkipConn = syncConnErrorTime > 0 1739 && (System.currentTimeMillis() - syncConnErrorTime) <= connCheckMs; 1740 if (isSkipConn) { 1741 continue; 1742 } 1743 } 1744 1745 String masterFingerprint = dds.getFingerprint(); 1746 String syncFingerprint = sds.getFingerprint(); 1747 if (masterFingerprint.equalsIgnoreCase(syncFingerprint)) { 1748 LoggerFactory.getLogger(DriverExecutor.class) 1749 .warn("Skip sync reason 'same connection fingerprint'."); 1750 continue; 1751 } 1752 1753 try { 1754 final Connection conn = sds.getConnection(); 1755 if (conn == null) { 1756 int errorCode = 99904; 1757 String errorMsg = "Connection is null."; 1758 throw new SQLException(errorMsg) { 1759 @Override 1760 public int getErrorCode() { 1761 return errorCode; 1762 } 1763 }; 1764 } 1765 conn.setAutoCommit(masterConn.getAutoCommit()); 1766 SYNC_CONN_ERROR_TIME.put(sdsHashCode, 0L); 1767 SYNC_EXECUTOR_MARK.get(masterConnHashCode).add(new DriverExecutor(conn)); 1768 } catch (SQLException e) { 1769 SYNC_CONN_ERROR_TIME.put(sdsHashCode, System.currentTimeMillis()); 1770 LoggerFactory.getLogger(DriverExecutor.class) 1771 .warn(String.format("ERROR CODE: (%s) %s", e.getErrorCode(), e.getMessage())); 1772 ConfigProperties ddscp = dds.getConfigProperties(); 1773 boolean connCheck = true; 1774 if (ddscp != null) { 1775 connCheck = ddscp.getBoolean("SyncConnectionCheck", true); 1776 } 1777 if (connCheck) 1778 throw e; 1779 } 1780 1781 } 1782 1783 return true; 1784 } 1785 } 1786 } 1787 return false; 1788 } 1789 1790 /** 1791 * For database replication 1792 * */ 1793 protected static void closeSyncConnection(Connection masterConn) throws SQLException { 1794 if (masterConn instanceof DriverConnection) { 1795 DriverConnection masterDriverConn = (DriverConnection) masterConn; 1796 Integer masterConnHashCode = masterDriverConn.getOriginConnectionHashCode(); 1797 1798 List<DriverExecutor> deList = SYNC_EXECUTOR_MARK.get(masterConnHashCode); 1799 if (deList != null) { 1800 for (DriverExecutor de : deList) { 1801 try { 1802 de.close(); 1803 } catch (SQLException e) { 1804 LoggerFactory.getLogger(DriverExecutor.class) 1805 .warn(String.format("ERROR CODE: (%s) %s", e.getErrorCode(), e.getMessage())); 1806 ConfigProperties ddscp = masterDriverConn.getDriverDataSource().getConfigProperties(); 1807 boolean connCheck = true; 1808 if (ddscp != null) { 1809 connCheck = ddscp.getBoolean("SyncConnectionCheck", true); 1810 } 1811 if (connCheck) 1812 throw e; 1813 } 1814 } 1815 SYNC_EXECUTOR_MARK.remove(masterConnHashCode); 1816 LoggerFactory.getLogger(DriverExecutor.class).debug("CloseSyncConnection - masterConn '{}' finished.", 1817 masterConnHashCode); 1818 } 1819 } 1820 } 1821 1822 /** 1823 * For database replication 1824 * */ 1825 protected static void commitSyncConnection(Connection masterConn) throws SQLException { 1826 if (masterConn instanceof DriverConnection) { 1827 DriverConnection masterDriverConn = (DriverConnection) masterConn; 1828 Integer masterConnHashCode = masterDriverConn.getOriginConnectionHashCode(); 1829 1830 List<DriverExecutor> deList = SYNC_EXECUTOR_MARK.get(masterConnHashCode); 1831 if (deList != null) { 1832 for (DriverExecutor de : deList) { 1833 try { 1834 de.commit(); 1835 } catch (SQLException e) { 1836 LoggerFactory.getLogger(DriverExecutor.class) 1837 .warn(String.format("ERROR CODE: (%s) %s", e.getErrorCode(), e.getMessage())); 1838 ConfigProperties ddscp = masterDriverConn.getDriverDataSource().getConfigProperties(); 1839 boolean connCheck = true; 1840 if (ddscp != null) { 1841 connCheck = ddscp.getBoolean("SyncConnectionCheck", true); 1842 } 1843 if (connCheck) 1844 throw e; 1845 } 1846 } 1847 LoggerFactory.getLogger(DriverExecutor.class).debug("CommitSyncConnection - masterConn '{}' finished.", 1848 masterConnHashCode); 1849 } 1850 } 1851 } 1852 1853 /** 1854 * For database replication 1855 * */ 1856 protected static void rollbackSyncConnection(Connection masterConn) throws SQLException { 1857 if (masterConn instanceof DriverConnection) { 1858 DriverConnection masterDriverConn = (DriverConnection) masterConn; 1859 Integer masterConnHashCode = masterDriverConn.getOriginConnectionHashCode(); 1860 1861 List<DriverExecutor> deList = SYNC_EXECUTOR_MARK.get(masterConnHashCode); 1862 if (deList != null) { 1863 for (DriverExecutor de : deList) { 1864 try { 1865 de.rollback(); 1866 } catch (SQLException e) { 1867 LoggerFactory.getLogger(DriverExecutor.class) 1868 .warn(String.format("ERROR CODE: (%s) %s", e.getErrorCode(), e.getMessage())); 1869 ConfigProperties ddscp = masterDriverConn.getDriverDataSource().getConfigProperties(); 1870 boolean connCheck = true; 1871 if (ddscp != null) { 1872 connCheck = ddscp.getBoolean("SyncConnectionCheck", true); 1873 } 1874 if (connCheck) 1875 throw e; 1876 } 1877 } 1878 LoggerFactory.getLogger(DriverExecutor.class) 1879 .debug("RollbackSyncConnection - masterConn '{}' finished.", masterConnHashCode); 1880 } 1881 } 1882 } 1883 1884 /** 1885 * For database replication 1886 * */ 1887 protected static void abortSyncConnection(Connection masterConn) throws SQLException { 1888 if (masterConn instanceof DriverConnection) { 1889 DriverConnection masterDriverConn = (DriverConnection) masterConn; 1890 Integer masterConnHashCode = masterDriverConn.getOriginConnectionHashCode(); 1891 1892 List<DriverExecutor> deList = SYNC_EXECUTOR_MARK.get(masterConnHashCode); 1893 if (deList != null) { 1894 for (DriverExecutor de : deList) { 1895 try { 1896 de.abort(); 1897 } catch (SQLException e) { 1898 LoggerFactory.getLogger(DriverExecutor.class) 1899 .warn(String.format("ERROR CODE: (%s) %s", e.getErrorCode(), e.getMessage())); 1900 ConfigProperties ddscp = masterDriverConn.getDriverDataSource().getConfigProperties(); 1901 boolean connCheck = true; 1902 if (ddscp != null) { 1903 connCheck = ddscp.getBoolean("SyncConnectionCheck", true); 1904 } 1905 if (connCheck) 1906 throw e; 1907 } 1908 } 1909 LoggerFactory.getLogger(DriverExecutor.class).debug("AbortSyncConnection - masterConn '{}' finished.", 1910 masterConnHashCode); 1911 } 1912 } 1913 } 1914 1915}