| 1 24 25 package org.objectweb.cjdbc.controller.virtualdatabase; 26 27 import java.sql.SQLException ; 28 import java.util.ArrayList ; 29 import java.util.Date ; 30 import java.util.Hashtable ; 31 import java.util.Iterator ; 32 import java.util.LinkedList ; 33 import java.util.Map ; 34 35 import javax.management.NotCompliantMBeanException ; 36 import javax.management.ObjectName ; 37 38 import org.objectweb.cjdbc.common.exceptions.BackupException; 39 import org.objectweb.cjdbc.common.exceptions.VirtualDatabaseException; 40 import org.objectweb.cjdbc.common.i18n.Translate; 41 import org.objectweb.cjdbc.common.jmx.JmxConstants; 42 import org.objectweb.cjdbc.common.jmx.JmxException; 43 import org.objectweb.cjdbc.common.jmx.mbeans.VirtualDatabaseMBean; 44 import org.objectweb.cjdbc.common.jmx.notifications.CjdbcNotificationList; 45 import org.objectweb.cjdbc.common.log.Trace; 46 import org.objectweb.cjdbc.common.monitor.backend.BackendStatistics; 47 import org.objectweb.cjdbc.common.shared.BackendInfo; 48 import org.objectweb.cjdbc.common.shared.BackendState; 49 import org.objectweb.cjdbc.common.shared.DumpInfo; 50 import org.objectweb.cjdbc.common.sql.AbstractWriteRequest; 51 import org.objectweb.cjdbc.common.sql.SelectRequest; 52 import org.objectweb.cjdbc.common.sql.StoredProcedure; 53 import org.objectweb.cjdbc.common.sql.filters.AbstractBlobFilter; 54 import org.objectweb.cjdbc.common.sql.schema.DatabaseSchema; 55 import org.objectweb.cjdbc.common.users.AdminUser; 56 import org.objectweb.cjdbc.common.users.VirtualDatabaseUser; 57 import org.objectweb.cjdbc.common.util.Constants; 58 import org.objectweb.cjdbc.common.util.ReadPrioritaryFIFOWriteLock; 59 import org.objectweb.cjdbc.common.xml.DatabasesXmlTags; 60 import org.objectweb.cjdbc.common.xml.XmlComponent; 61 import org.objectweb.cjdbc.common.xml.XmlTools; 62 import org.objectweb.cjdbc.controller.authentication.AuthenticationManager; 63 import org.objectweb.cjdbc.controller.backend.DatabaseBackend; 64 import org.objectweb.cjdbc.controller.backup.Backuper; 65 import org.objectweb.cjdbc.controller.cache.result.AbstractResultCache; 66 import org.objectweb.cjdbc.controller.core.Controller; 67 import org.objectweb.cjdbc.controller.core.shutdown.VirtualDatabaseForceShutdownThread; 68 import org.objectweb.cjdbc.controller.core.shutdown.VirtualDatabaseSafeShutdownThread; 69 import org.objectweb.cjdbc.controller.core.shutdown.VirtualDatabaseShutdownThread; 70 import org.objectweb.cjdbc.controller.core.shutdown.VirtualDatabaseWaitShutdownThread; 71 import org.objectweb.cjdbc.controller.jmx.AbstractStandardMBean; 72 import org.objectweb.cjdbc.controller.jmx.MBeanServerManager; 73 import org.objectweb.cjdbc.controller.jmx.RmiConnector; 74 import org.objectweb.cjdbc.controller.loadbalancer.AllBackendsFailedException; 75 import org.objectweb.cjdbc.controller.monitoring.SQLMonitoring; 76 import org.objectweb.cjdbc.controller.recoverylog.BackendRecoveryInfo; 77 import org.objectweb.cjdbc.controller.recoverylog.RecoverThread; 78 import org.objectweb.cjdbc.controller.recoverylog.RecoveryLog; 79 import org.objectweb.cjdbc.controller.requestmanager.RAIDbLevels; 80 import org.objectweb.cjdbc.controller.requestmanager.RequestManager; 81 82 96 public class VirtualDatabase extends AbstractStandardMBean 97 implements 98 VirtualDatabaseMBean, 99 XmlComponent 100 { 101 private static final long serialVersionUID = 1399418136380336827L; 102 103 116 117 protected String name; 118 119 123 protected AuthenticationManager authenticationManager; 124 125 126 protected ArrayList backends; 127 128 129 protected ReadPrioritaryFIFOWriteLock rwLock; 130 131 132 protected RequestManager requestManager; 133 134 135 protected LinkedList totalOrderQueue = null; 136 137 138 public Trace logger = null; 139 protected Trace requestLogger = null; 140 141 private ArrayList activeThreads = new ArrayList (); 143 private int idleThreads = 0; 145 private ArrayList pendingConnections = new ArrayList (); 147 148 149 protected int maxNbOfConnections; 150 151 152 protected boolean poolConnectionThreads; 153 154 155 protected long maxThreadIdleTime; 156 157 161 protected int minNbOfThreads; 162 163 164 protected int maxNbOfThreads; 165 166 167 protected int currentNbOfThreads; 168 169 170 protected VirtualDatabaseDynamicMetaData metadata; 171 private VirtualDatabaseStaticMetaData staticMetadata; 172 173 private SQLMonitoring sqlMonitor = null; 174 175 176 public static final int CHECK_BACKEND_ENABLE = 1; 177 178 public static final int CHECK_BACKEND_DISABLE = 0; 179 180 public static final int NO_CHECK_BACKEND = -1; 181 182 183 private int sqlShortFormLength; 184 185 186 private AbstractBlobFilter blobFilter; 187 188 189 Controller controller; 190 191 192 private String databaseProductNames = "C-JDBC"; 193 194 195 private boolean shuttingDown = false; 196 197 198 199 217 public VirtualDatabase(Controller controller, String name, 218 int maxConnections, boolean pool, int minThreads, int maxThreads, 219 long maxThreadIdleTime, int sqlShortFormLength, 220 AbstractBlobFilter blobFilter) throws NotCompliantMBeanException , 221 JmxException 222 { 223 super(VirtualDatabaseMBean.class); 224 this.controller = controller; 225 this.name = name; 226 this.maxNbOfConnections = maxConnections; 227 this.poolConnectionThreads = pool; 228 this.minNbOfThreads = minThreads; 229 this.maxNbOfThreads = maxThreads; 230 this.maxThreadIdleTime = maxThreadIdleTime; 231 this.sqlShortFormLength = sqlShortFormLength; 232 this.blobFilter = blobFilter; 233 backends = new ArrayList (); 234 235 ObjectName objectName = JmxConstants.getVirtualDbObjectName(name); 236 MBeanServerManager.registerMBean(this, objectName); 237 238 rwLock = new ReadPrioritaryFIFOWriteLock(); 239 logger = Trace.getLogger("org.objectweb.cjdbc.controller.virtualdatabase." 240 + name); 241 requestLogger = Trace 242 .getLogger("org.objectweb.cjdbc.controller.virtualdatabase.request." 243 + name); 244 } 245 246 253 public final void acquireReadLockBackendLists() throws InterruptedException  254 { 255 rwLock.acquireRead(); 256 } 257 258 263 public final void releaseReadLockBackendLists() 264 { 265 rwLock.releaseRead(); 266 } 267 268 273 public boolean isDistributed() 274 { 275 return false; 276 } 277 278 279 280 289 public boolean checkUserAuthentication(String virtualLogin, 290 String virtualPassword) 291 { 292 if (authenticationManager == null) 293 { 294 logger.error("No authentification manager defined to check login '" 295 + virtualLogin + "'"); 296 return false; 297 } 298 else 299 return authenticationManager.isValidVirtualUser(new VirtualDatabaseUser( 300 virtualLogin, virtualPassword)); 301 } 302 303 312 public boolean checkAdminAuthentication(String adminLogin, 313 String adminPassword) 314 { 315 if (authenticationManager == null) 316 { 317 logger.error("No authentification manager defined to check admin login '" 318 + adminLogin + "'"); 319 return false; 320 } 321 else 322 return authenticationManager.isValidAdminUser(new AdminUser(adminLogin, 323 adminPassword)); 324 } 325 326 333 public ControllerResultSet execReadRequest(SelectRequest request) 334 throws SQLException  335 { 336 if (request == null) 337 { 338 String msg = "Request failed (null read request received)"; 339 logger.warn(msg); 340 throw new SQLException (msg); 341 } 342 343 try 344 { 345 if (requestLogger.isInfoEnabled()) 346 requestLogger.info("S " + request.getTransactionId() + " " 347 + request.getSQL()); 348 349 long start = 0; 350 if (sqlMonitor != null && sqlMonitor.isActive()) 351 start = System.currentTimeMillis(); 352 353 ControllerResultSet rs = requestManager.execReadRequest(request); 354 355 if (sqlMonitor != null && sqlMonitor.isActive()) 356 sqlMonitor.logRequestTime(request, System.currentTimeMillis() - start); 357 358 return rs; 359 } 360 catch (SQLException e) 361 { 362 String msg = "Request '" + request.getId() + "' failed (" 363 + e.getMessage() + ")"; 364 logger.warn(msg); 365 if (sqlMonitor != null && sqlMonitor.isActive()) 366 sqlMonitor.logError(request); 367 throw e; 368 } 369 } 370 371 378 public int execWriteRequest(AbstractWriteRequest request) throws SQLException  379 { 380 if (request == null) 381 { 382 String msg = "Request failed (null write request received)"; 383 logger.warn(msg); 384 throw new SQLException (msg); 385 } 386 387 try 388 { 389 if (requestLogger.isInfoEnabled()) 390 requestLogger.info("W " + request.getTransactionId() + " " 391 + request.getSQL()); 392 393 long start = 0; 394 if (sqlMonitor != null && sqlMonitor.isActive()) 395 start = System.currentTimeMillis(); 396 397 int result = requestManager.execWriteRequest(request); 398 399 if (sqlMonitor != null && sqlMonitor.isActive()) 400 sqlMonitor.logRequestTime(request, System.currentTimeMillis() - start); 401 402 return result; 403 } 404 catch (SQLException e) 405 { 406 String msg = "Request '" + request.getId() + "' failed (" 407 + e.getMessage() + ")"; 408 logger.warn(msg); 409 if (sqlMonitor != null && sqlMonitor.isActive()) 410 sqlMonitor.logError(request); 411 throw e; 412 } 413 } 414 415 422 public ControllerResultSet execWriteRequestWithKeys( 423 AbstractWriteRequest request) throws SQLException  424 { 425 if (request == null) 426 { 427 String msg = "Request failed (null write request received)"; 428 logger.warn(msg); 429 throw new SQLException (msg); 430 } 431 432 try 433 { 434 if (requestLogger.isInfoEnabled()) 435 requestLogger.info("W " + request.getTransactionId() + " " 436 + request.getSQL()); 437 438 long start = 0; 439 if (sqlMonitor != null && sqlMonitor.isActive()) 440 start = System.currentTimeMillis(); 441 442 ControllerResultSet result = requestManager 443 .execWriteRequestWithKeys(request); 444 445 if (sqlMonitor != null && sqlMonitor.isActive()) 446 sqlMonitor.logRequestTime(request, System.currentTimeMillis() - start); 447 448 return result; 449 } 450 catch (SQLException e) 451 { 452 String msg = "Request '" + request.getId() + "' failed (" 453 + e.getMessage() + ")"; 454 logger.warn(msg); 455 if (sqlMonitor != null && sqlMonitor.isActive()) 456 sqlMonitor.logError(request); 457 throw e; 458 } 459 } 460 461 468 public ControllerResultSet execReadStoredProcedure(StoredProcedure proc) 469 throws SQLException  470 { 471 if (proc == null) 472 { 473 String msg = "Request failed (null stored procedure received)"; 474 logger.warn(msg); 475 throw new SQLException (msg); 476 } 477 478 try 479 { 480 if (requestLogger.isInfoEnabled()) 481 requestLogger 482 .info("S " + proc.getTransactionId() + " " + proc.getSQL()); 483 484 long start = 0; 485 if (sqlMonitor != null && sqlMonitor.isActive()) 486 start = System.currentTimeMillis(); 487 488 ControllerResultSet rs = requestManager.execReadStoredProcedure(proc); 489 490 if (sqlMonitor != null && sqlMonitor.isActive()) 491 sqlMonitor.logRequestTime(proc, System.currentTimeMillis() - start); 492 493 return rs; 494 } 495 catch (AllBackendsFailedException e) 496 { 497 String msg = Translate.get( 498 "loadbalancer.storedprocedure.failed.on.all.backends", new String []{ 499 String.valueOf(proc.getId()), e.getMessage()}); 500 logger.warn(msg); 501 if (sqlMonitor != null && sqlMonitor.isActive()) 502 sqlMonitor.logError(proc); 503 throw new SQLException (msg); 504 } 505 catch (SQLException e) 506 { 507 String msg = Translate.get("loadbalancer.storedprocedure.failed", 508 new String []{String.valueOf(proc.getId()), e.getMessage()}); 509 logger.warn(msg); 510 if (sqlMonitor != null && sqlMonitor.isActive()) 511 sqlMonitor.logError(proc); 512 throw e; 513 } 514 } 515 516 523 protected int execWriteStoredProcedure(StoredProcedure proc) 524 throws SQLException  525 { 526 if (proc == null) 527 { 528 String msg = "Request failed (null stored procedure received)"; 529 logger.warn(msg); 530 throw new SQLException (msg); 531 } 532 533 try 534 { 535 if (requestLogger.isInfoEnabled()) 536 requestLogger 537 .info("W " + proc.getTransactionId() + " " + proc.getSQL()); 538 539 long start = 0; 540 if (sqlMonitor != null && sqlMonitor.isActive()) 541 start = System.currentTimeMillis(); 542 543 int result = requestManager.execWriteStoredProcedure(proc); 544 545 if (sqlMonitor != null && sqlMonitor.isActive()) 546 sqlMonitor.logRequestTime(proc, System.currentTimeMillis() - start); 547 548 return result; 549 } 550 catch (AllBackendsFailedException e) 551 { 552 String msg = Translate.get( 553 "loadbalancer.storedprocedure.failed.on.all.backends", new String []{ 554 String.valueOf(proc.getId()), e.getMessage()}); 555 logger.warn(msg); 556 if (sqlMonitor != null && sqlMonitor.isActive()) 557 sqlMonitor.logError(proc); 558 throw new SQLException (msg); 559 } 560 catch (SQLException e) 561 { 562 String msg = Translate.get("loadbalancer.storedprocedure.failed", 563 new String []{String.valueOf(proc.getId()), e.getMessage()}); 564 logger.warn(msg); 565 if (sqlMonitor != null && sqlMonitor.isActive()) 566 sqlMonitor.logError(proc); 567 throw e; 568 } 569 } 570 571 572 573 587 public long begin(String login) throws SQLException  588 { 589 try 590 { 591 long tid = requestManager.begin(login); 592 if (requestLogger.isInfoEnabled()) 593 requestLogger.info("B " + tid); 594 return tid; 595 } 596 catch (SQLException e) 597 { 598 String msg = "Begin failed (" + e.getMessage() + ")"; 599 logger.warn(msg); 600 throw e; 601 } 602 } 603 604 615 public void abort(long transactionId, boolean logAbort) throws SQLException  616 { 617 requestManager.abort(transactionId, logAbort); 618 if (requestLogger.isInfoEnabled()) 620 requestLogger.info("R " + transactionId); 621 } 622 623 630 public void commit(long transactionId, boolean logCommit) throws SQLException  631 { 632 try 633 { 634 if (requestLogger.isInfoEnabled()) 635 requestLogger.info("C " + transactionId); 636 requestManager.commit(transactionId, logCommit); 637 } 638 catch (SQLException e) 639 { 640 String msg = "Commit of transaction '" + transactionId + "' failed (" 641 + e.getMessage() + ")"; 642 logger.warn(msg); 643 throw e; 644 } 645 } 646 647 655 public void rollback(long transactionId, boolean logRollback) 656 throws SQLException  657 { 658 try 659 { 660 if (requestLogger.isInfoEnabled()) 661 requestLogger.info("R " + transactionId); 662 requestManager.rollback(transactionId, logRollback); 663 } 664 catch (SQLException e) 665 { 666 String msg = "Rollback of transaction '" + transactionId + "' failed (" 667 + e.getMessage() + ")"; 668 logger.warn(msg); 669 throw e; 670 } 671 } 672 673 680 public void rollback(long transactionId, String savepointName) 681 throws SQLException  682 { 683 try 684 { 685 if (requestLogger.isInfoEnabled()) 686 requestLogger.info("R " + transactionId + " " + savepointName); 687 requestManager.rollback(transactionId, savepointName); 688 } 689 catch (SQLException e) 690 { 691 String msg = "Rollback to savepoint '" + savepointName + "' for " 692 + "transaction '" + transactionId + "' failed (" + e.getMessage() 693 + ")"; 694 logger.warn(msg); 695 throw e; 696 } 697 } 698 699 706 public int setSavepoint(long transactionId) throws SQLException  707 { 708 try 709 { 710 int savepointId = requestManager.setSavepoint(transactionId); 711 if (requestLogger.isInfoEnabled()) 712 requestLogger.info("P " + transactionId + " " + savepointId); 713 return savepointId; 714 } 715 catch (SQLException e) 716 { 717 String msg = "Setting unnamed savepoint to transaction '" + transactionId 718 + "' failed (" + e.getMessage() + ")"; 719 logger.warn(msg); 720 throw e; 721 } 722 } 723 724 731 public void setSavepoint(long transactionId, String name) throws SQLException  732 { 733 try 734 { 735 if (requestLogger.isInfoEnabled()) 736 requestLogger.info("P " + transactionId + " " + name); 737 requestManager.setSavepoint(transactionId, name); 738 } 739 catch (SQLException e) 740 { 741 String msg = "Setting savepoint with name '" + name + "' to transaction " 742 + "'" + transactionId + "' failed (" + e.getMessage() + ")"; 743 logger.warn(msg); 744 throw e; 745 } 746 } 747 748 755 public void releaseSavepoint(long transactionId, String name) 756 throws SQLException  757 { 758 try 759 { 760 if (requestLogger.isInfoEnabled()) 761 requestLogger.info("F " + transactionId + " " + name); 762 requestManager.releaseSavepoint(transactionId, name); 763 } 764 catch (SQLException e) 765 { 766 String msg = "Releasing savepoint with name '" + name + "' from " 767 + "transaction '" + transactionId + "' failed (" + e.getMessage() 768 + ")"; 769 logger.warn(msg); 770 throw e; 771 } 772 } 773 774 778 784 public void addBackend(DatabaseBackend db) throws VirtualDatabaseException 785 { 786 this.addBackend(db, true); 787 } 788 789 796 public void addBackend(DatabaseBackend db, boolean checkForCompliance) 797 throws VirtualDatabaseException 798 { 799 if (db == null) 800 { 801 String msg = "Illegal null database backend in addBackend(DatabaseBackend) method"; 802 logger.error(msg); 803 throw new VirtualDatabaseException(msg); 804 } 805 806 if (db.isReadEnabled()) 807 { 808 String msg = "It is not allowed to add an enabled database."; 809 logger.error(msg); 810 throw new VirtualDatabaseException(msg); 811 } 812 813 try 815 { 816 rwLock.acquireWrite(); 817 } 818 catch (InterruptedException e) 819 { 820 String msg = Translate.get( 821 "loadbalancer.backendlist.acquire.writelock.failed", e); 822 logger.error(msg); 823 throw new VirtualDatabaseException(msg); 824 } 825 826 if (backends.indexOf(db) != -1) 828 { 829 rwLock.releaseWrite(); 830 String msg = "Duplicate backend " + db.getURL(); 831 logger.warn(msg); 832 throw new VirtualDatabaseException(msg); 833 } 834 835 ArrayList logins = authenticationManager.getVirtualLogins(); 837 VirtualDatabaseUser vdu; 838 String login; 839 for (int i = 0; i < logins.size(); i++) 840 { 841 vdu = (VirtualDatabaseUser) logins.get(i); 842 login = vdu.getLogin(); 843 if (db.getConnectionManager(login) == null) 844 { 845 rwLock.releaseWrite(); 846 throw new VirtualDatabaseException(Translate.get( 847 "backend.missing.connection.manager", login)); 848 } 849 } 850 851 try 853 { 854 if (logger.isDebugEnabled()) 855 logger.debug("Checking driver compliance"); 856 if (checkForCompliance) 857 db.checkDriverCompliance(); } 859 catch (Exception e) 860 { 861 rwLock.releaseWrite(); 862 String msg = "Error while adding database backend " + db.getName() + " (" 863 + e + ")"; 864 logger.warn(msg); 865 throw new VirtualDatabaseException(msg); 866 } 867 868 db.setSqlShortFormLength(getSQLShortFormLength()); 869 870 backends.add(db); 872 if (logger.isDebugEnabled()) 873 logger.debug("Backend " + db.getName() + " added successfully"); 874 875 885 if (getRequestManager() != null) 886 { 887 db.setStateListener(getRequestManager().getBackendStateListener()); 888 } 889 890 rwLock.releaseWrite(); 892 893 if (MBeanServerManager.isJmxEnabled()) 895 { 896 Hashtable data = new Hashtable (); 898 data.put(CjdbcNotificationList.DATA_DATABASE, this.name); 899 data.put(CjdbcNotificationList.DATA_DRIVER, db.getDriverClassName()); 900 String checkpoint = db.getLastKnownCheckpoint(); 901 checkpoint = (checkpoint == null) ? "" : checkpoint; 902 data.put(CjdbcNotificationList.DATA_CHECKPOINT, checkpoint); 903 data.put(CjdbcNotificationList.DATA_NAME, db.getName()); 904 data.put(CjdbcNotificationList.DATA_URL, db.getURL()); 905 RmiConnector.broadcastNotification(this, 906 CjdbcNotificationList.VIRTUALDATABASE_BACKEND_ADDED, 907  
|