| 1 24 25 package org.objectweb.cjdbc.controller.backend; 26 27 import java.io.StringReader ; 28 import java.net.ConnectException ; 29 import java.sql.Connection ; 30 import java.sql.SQLException ; 31 import java.sql.Savepoint ; 32 import java.sql.Statement ; 33 import java.util.ArrayList ; 34 import java.util.HashMap ; 35 import java.util.Hashtable ; 36 import java.util.Iterator ; 37 import java.util.List ; 38 import java.util.Map ; 39 import java.util.Vector ; 40 41 import javax.management.NotCompliantMBeanException ; 42 43 import org.dom4j.Document; 44 import org.dom4j.Element; 45 import org.dom4j.io.SAXReader; 46 import org.objectweb.cjdbc.common.exceptions.NoTransactionStartWhenDisablingException; 47 import org.objectweb.cjdbc.common.exceptions.UnreachableBackendException; 48 import org.objectweb.cjdbc.common.i18n.Translate; 49 import org.objectweb.cjdbc.common.jmx.mbeans.DatabaseBackendMBean; 50 import org.objectweb.cjdbc.common.jmx.notifications.CjdbcNotificationList; 51 import org.objectweb.cjdbc.common.log.Trace; 52 import org.objectweb.cjdbc.common.monitor.backend.BackendStatistics; 53 import org.objectweb.cjdbc.common.shared.BackendInfo; 54 import org.objectweb.cjdbc.common.shared.BackendState; 55 import org.objectweb.cjdbc.common.sql.AbstractWriteRequest; 56 import org.objectweb.cjdbc.common.sql.CreateRequest; 57 import org.objectweb.cjdbc.common.sql.metadata.MetadataContainer; 58 import org.objectweb.cjdbc.common.sql.schema.DatabaseSchema; 59 import org.objectweb.cjdbc.common.sql.schema.DatabaseTable; 60 import org.objectweb.cjdbc.common.xml.DatabasesXmlTags; 61 import org.objectweb.cjdbc.common.xml.XmlComponent; 62 import org.objectweb.cjdbc.controller.backend.rewriting.AbstractRewritingRule; 63 import org.objectweb.cjdbc.controller.connection.AbstractConnectionManager; 64 import org.objectweb.cjdbc.controller.connection.FailFastPoolConnectionManager; 65 import org.objectweb.cjdbc.controller.connection.RandomWaitPoolConnectionManager; 66 import org.objectweb.cjdbc.controller.connection.SimpleConnectionManager; 67 import org.objectweb.cjdbc.controller.connection.VariablePoolConnectionManager; 68 import org.objectweb.cjdbc.controller.jmx.AbstractStandardMBean; 69 import org.objectweb.cjdbc.controller.jmx.MBeanServerManager; 70 import org.objectweb.cjdbc.controller.jmx.RmiConnector; 71 import org.objectweb.cjdbc.controller.loadbalancer.AbstractLoadBalancer; 72 73 86 public final class DatabaseBackend extends AbstractStandardMBean 87 implements 88 XmlComponent, 89 DatabaseBackendMBean 90 { 91 102 103 private String name; 104 105 106 private String driverPath; 107 108 109 private String driverClassName; 110 111 112 private transient DriverCompliance driverCompliance; 113 114 115 private String url; 116 117 118 private String virtualDatabaseName; 119 120 121 private boolean writeCanBeEnabled; 122 123 124 private String connectionTestStatement; 125 126 130 private transient DatabaseSchema schema; 131 132 133 private boolean schemaIsStatic = false; 134 135 139 private boolean schemaIsNeededByVdb = true; 140 141 142 private boolean schemaIsDirty = true; 143 144 145 private transient HashMap connectionManagers; 146 147 148 protected transient Trace logger; 149 150 151 private transient ArrayList activeTransactions = new ArrayList (); 152 153 154 private transient Map savepoints = new HashMap (); 155 156 157 private transient Vector pendingRequests = new Vector (); 158 159 160 private int totalRequest; 161 private int totalWriteRequest; 162 private int totalReadRequest; 163 private int totalTransactions; 164 165 166 private ArrayList rewritingRules; 167 168 169 private int dynamicPrecision; 170 private boolean gatherSystemTables = false; 171 private String schemaName = null; 172 173 174 private int sqlShortFormLength = 40; 175 176 private String lastKnownCheckpoint; 177 178 183 private int state = BackendState.DISABLED; 184 185 private transient BackendStateListener stateListener; 186 187 202 public DatabaseBackend(String name, String driverPath, 203 String driverClassName, String url, String vdbName, 204 boolean writeCanBeEnabled, String connectionTestStatement) 205 throws NotCompliantMBeanException  206 { 207 super(DatabaseBackendMBean.class); 208 if (name == null) 209 throw new IllegalArgumentException (Translate 210 .get("backend.null.backend.name")); 211 212 if (driverClassName == null) 213 throw new IllegalArgumentException (Translate.get("backend.null.driver")); 214 215 if (url == null) 216 throw new IllegalArgumentException (Translate.get("backend.null.url")); 217 218 if (vdbName == null) 219 throw new IllegalArgumentException (Translate 220 .get("backend.null.virtualdatabase.name")); 221 222 if (connectionTestStatement == null) 223 throw new IllegalArgumentException (Translate 224 .get("backend.null.connection.test")); 225 226 this.name = name; 227 this.writeCanBeEnabled = writeCanBeEnabled; 228 this.driverPath = driverPath; 229 this.driverClassName = driverClassName; 230 this.url = url; 231 this.virtualDatabaseName = vdbName; 232 this.connectionTestStatement = connectionTestStatement; 233 this.connectionManagers = new HashMap (); 234 logger = Trace 235 .getLogger("org.objectweb.cjdbc.controller.backend.DatabaseBackend." 236 + name); 237 this.driverCompliance = new DriverCompliance(logger); 238 totalRequest = 0; 239 dynamicPrecision = DatabaseBackendSchemaConstants.DynamicPrecisionAll; 240 } 241 242 249 public DatabaseBackend(BackendInfo info) throws NotCompliantMBeanException  250 { 251 this(info.getName(), info.getDriverPath(), info.getDriverClassName(), info 252 .getUrl(), info.getVirtualDatabaseName(), true, info 253 .getConnectionTestStatement()); 254 setDynamicPrecision(info.getDynamicPrecision(), 255 info.isGatherSystemTables(), info.getSchemaName()); 256 try 257 { 258 String xml = info.getXml(); 259 StringReader sreader = new StringReader (xml); 260 SAXReader reader = new SAXReader(); 261 Document document = reader.read(sreader); 262 Element root = document.getRootElement(); 263 Iterator iter1 = root.elementIterator(); 264 while (iter1.hasNext()) 265 { 266 Element elem = (Element) iter1.next(); 267 if (elem.getName().equals(DatabasesXmlTags.ELT_ConnectionManager)) 268 { 269 String vuser = elem.valueOf("@" + DatabasesXmlTags.ATT_vLogin); 270 String rlogin = elem.valueOf("@" + DatabasesXmlTags.ATT_rLogin); 271 String rpassword = elem.valueOf("@" + DatabasesXmlTags.ATT_rPassword); 272 Iterator iter2 = elem.elementIterator(); 273 while (iter2.hasNext()) 274 { 275 Element connectionManager = (Element) iter2.next(); 276 String cname = connectionManager.getName(); 277 if (cname 278 .equals(DatabasesXmlTags.ELT_VariablePoolConnectionManager)) 279 { 280 int minPoolSize = Integer.parseInt(connectionManager.valueOf("@" 281 + DatabasesXmlTags.ATT_minPoolSize)); 282 int maxPoolSize = Integer.parseInt(connectionManager.valueOf("@" 283 + DatabasesXmlTags.ATT_maxPoolSize)); 284 int idleTimeout = Integer.parseInt(connectionManager.valueOf("@" 285 + DatabasesXmlTags.ATT_idleTimeout)); 286 int waitTimeout = Integer.parseInt(connectionManager.valueOf("@" 287 + DatabasesXmlTags.ATT_waitTimeout)); 288 this.addConnectionManager(vuser, 289 new VariablePoolConnectionManager(url, name, rlogin, 290 rpassword, driverPath, driverClassName, minPoolSize, 291 maxPoolSize, idleTimeout, waitTimeout)); 292 } 293 else if (cname.equals(DatabasesXmlTags.ELT_SimpleConnectionManager)) 294 { 295 this.addConnectionManager(vuser, new SimpleConnectionManager(url, 296 name, rlogin, rpassword, driverPath, driverClassName)); 297 } 298 else if (cname 299 .equals(DatabasesXmlTags.ELT_RandomWaitPoolConnectionManager)) 300 { 301 int poolSize = Integer.parseInt(connectionManager.valueOf("@" 302 + DatabasesXmlTags.ATT_poolSize)); 303 int timeout = Integer.parseInt(connectionManager.valueOf("@" 304 + DatabasesXmlTags.ATT_timeout)); 305 this 306 .addConnectionManager(vuser, 307 new RandomWaitPoolConnectionManager(url, name, rlogin, 308 rpassword, driverPath, driverClassName, poolSize, 309 timeout)); 310 } 311 else if (cname 312 .equals(DatabasesXmlTags.ELT_FailFastPoolConnectionManager)) 313 { 314 int poolSize = Integer.parseInt(connectionManager.valueOf("@" 315 + DatabasesXmlTags.ATT_poolSize)); 316 this.addConnectionManager(vuser, 317 new FailFastPoolConnectionManager(url, name, rlogin, 318 rpassword, driverPath, driverClassName, poolSize)); 319 } 320 } 321 } 322 } 323 324 } 325 catch (Exception e) 326 { 327 logger 328 .error(Translate.get("backend.add.connection.manager.failed", e), e); 329 } 330 } 331 332 349 public DatabaseBackend(String name, String driverPath, 350 String driverClassName, String url, String vdbName, 351 String connectionTestStatement, String dynamicSchemaLevel) 352 throws NotCompliantMBeanException  353 { 354 this(name, driverPath, driverClassName, url, vdbName, true, 355 connectionTestStatement); 356 this.dynamicPrecision = DatabaseBackendSchemaConstants 357 .getDynamicSchemaLevel(dynamicSchemaLevel); 358 } 359 360 365 public void setSqlShortFormLength(int sqlShortFormLength) 366 { 367 this.sqlShortFormLength = sqlShortFormLength; 368 } 369 370 376 public int getSQLShortFormLength() 377 { 378 return sqlShortFormLength; 379 } 380 381 382 383 390 public boolean equals(Object other) 391 { 392 if ((other == null) || (!(other instanceof DatabaseBackend))) 393 return false; 394 else 395 { 396 DatabaseBackend b = (DatabaseBackend) other; 397 return name.equals(b.getName()) 398 && driverClassName.equals(b.getDriverClassName()) 399 && url.equals(b.getURL()); 400 } 401 } 402 403 413 public boolean hasTables(ArrayList tables) 414 { 415 DatabaseSchema schemaPtr = getDatabaseSchema(); 416 if (schemaPtr == null) 417 throw new NullPointerException (Translate.get("backend.schema.not.set")); 418 419 if (tables == null) 420 throw new IllegalArgumentException (Translate.get("backend.null.tables")); 421 422 int size = tables.size(); 423 for (int i = 0; i < size; i++) 424 { 425 if (!schemaPtr.hasTable((String ) tables.get(i))) 426 return false; 427 } 428 return true; 429 } 430 431 440 public boolean hasTable(String table) 441 { 442 DatabaseSchema schemaPtr = getDatabaseSchema(); 443 if (schemaPtr == null) 444 throw new NullPointerException (Translate.get("backend.schema.not.set")); 445 446 return schemaPtr.hasTable(table); 447 } 448 449 454 public ArrayList getTables() 455 { 456 DatabaseSchema schemaPtr = getDatabaseSchema(); 457 if (schemaPtr == null) 458 throw new NullPointerException (Translate.get("backend.schema.not.set")); 459 return schemaPtr.getTables(); 460 } 461 462 471 public boolean hasStoredProcedure(String procedureName) 472 { 473 DatabaseSchema schemaPtr = getDatabaseSchema(); 474 if (schemaPtr == null) 475 throw new NullPointerException (Translate.get("backend.schema.not.set")); 476 477 return schemaPtr.hasProcedure(procedureName); 478 } 479 480 481 482 488 public synchronized void initializeConnections() throws SQLException  489 { 490 if (connectionManagers.isEmpty()) 491 throw new SQLException (Translate.get("backend.not.defined", new String []{ 492 name, url})); 493 494 AbstractConnectionManager connectionManager; 495 Iterator iter = connectionManagers.values().iterator(); 496 while (iter.hasNext()) 497 { 498 connectionManager = (AbstractConnectionManager) iter.next(); 499 if (!connectionManager.isInitialized()) 500 connectionManager.initializeConnections(); 501 } 502 } 503 504 510 public synchronized void finalizeConnections() throws SQLException  511 { 512 if (connectionManagers.isEmpty()) 513 throw new SQLException (Translate.get("backend.not.defined", new String []{ 514 name, url})); 515 516 AbstractConnectionManager connectionManager; 517 Iterator iter = connectionManagers.values().iterator(); 518 while (iter.hasNext()) 519 { 520 connectionManager = (AbstractConnectionManager) iter.next(); 521 if (connectionManager.isInitialized()) 522 connectionManager.finalizeConnections(); 523 } 524 } 525 526 535 public boolean isValidConnection(Connection connection) 536 { 537 try 538 { 539 Statement s = connection.createStatement(); 540 s.executeQuery(connectionTestStatement); 541 } 542 catch (SQLException e) 543 { 544 if ("25P02".equals(e.getSQLState()) 545 || (e.getMessage() != null && e 546 .getMessage() 547 .indexOf( 548 "current transaction is aborted, queries ignored until end of transaction block") > 0)) 549 { 550 return true; 559 } 560 return false; 561 } 562 return true; 563 } 564 565 572 public void addConnectionManager(String vLogin, 573 AbstractConnectionManager connectionManager) 574 { 575 if (connectionManager == null) 576 throw new IllegalArgumentException (Translate.get( 577 "backend.null.connection.manager", new String []{name, url})); 578 if (logger.isInfoEnabled()) 579 logger.info(Translate.get("backend.add.connection.manager.for.user", 580 vLogin)); 581 connectionManager.setVLogin(vLogin); 582 connectionManagers.put(vLogin, connectionManager); 583 } 584 585 603 public synchronized Connection getConnectionForTransactionAndLazyBeginIfNeeded( 604 Long tid, AbstractConnectionManager cm, int transactionIsolationLevel) 605 throws UnreachableBackendException, 606 NoTransactionStartWhenDisablingException, SQLException  607 { 608 if (isStartedTransaction(tid)) 609 { return cm.retrieveConnection(tid.longValue()); 611 } 612 else 613 { 614 if (isDisabling()) 615 throw new NoTransactionStartWhenDisablingException(); 616 617 startTransaction(tid); 619 620 return AbstractLoadBalancer.getConnectionAndBeginTransaction(this, cm, 622 tid.longValue(), transactionIsolationLevel); 623 } 624 } 625 626 627 628 634 public void startTransaction(Long tid) 635 { 636 synchronized (activeTransactions) 637 { 638 totalTransactions++; 639 activeTransactions.add(tid); 640 } 641 } 642 643 649 public void stopTransaction(Long tid) 650 { 651 synchronized (activeTransactions) 652 { 653 if (!activeTransactions.remove(tid)) 654 throw new IllegalArgumentException (Translate.get( 655 "backend.transaction.not.started", new String []{"" + tid, name})); 656 if (activeTransactions.isEmpty()) 659 { 660 activeTransactions.notifyAll(); 661 } 662 } 663 664 synchronized (savepoints) 665 { 666 savepoints.remove(tid); 667 } 668 } 669 670 675 public void waitForAllTransactionsToComplete() 676 { 677 synchronized (activeTransactions) 678 { 679 if (activeTransactions.isEmpty()) 680 return; 681 else 682 try 683 { 684 activeTransactions.wait(); 685 } 686 catch (InterruptedException ignore) 687 { 688 } 689 } 690 } 691 692 699 public boolean isStartedTransaction(Long tid) 700 { 701 synchronized (activeTransactions) 702 { 703 return activeTransactions.contains(tid); 704 } 705 } 706 707 713 public void addSavepoint(Long tid, Savepoint savepoint) 714 { 715 synchronized (savepoints) 716 { 717 List savepointList = (List ) savepoints.get(tid); 718 if (savepointList == null) 719 { savepointList = new ArrayList (); 721 savepoints.put(tid, savepointList); 722 } 723 savepointList.add(savepoint); 724 } 725 } 726 727 733 public void removeSavepoint(Long tid, Savepoint savepoint) 734 { 735 synchronized (savepoints) 736 { 737 List savepointList = (List ) savepoints.get(tid); 738 if (savepointList == null) 739 logger.error("No savepoints found for transaction " + tid); 740 else 741 savepointList.remove(savepoint); 742 } 743 } 744 745 752 public Savepoint getSavepoint(Long tid, String savepointName) 753 { 754 synchronized (savepoints) 755 { 756 List savepointList = (List ) savepoints.get(tid); 757 if (savepointList == null) 758 return null; 760 Iterator i = savepointList.iterator(); 761 while (i.hasNext()) 762 { 763 try 764 { 765 Savepoint savepoint = (Savepoint ) i.next(); 766 if (savepointName.equals(savepoint.getSavepointName())) 767 return savepoint; 768 } 769 catch (SQLException ignore) 770 { 771 } 774 } 775 } 776 777 return null; 779 } 780 781 790 public boolean hasSavepointForTransaction(Long tid, String savepointName) 791 { 792 return (this.getSavepoint(tid, savepointName) != null); 793 } 794 795 801 public synchronized boolean isInitialized() throws SQLException  802 { 803 if (connectionManagers.isEmpty()) 804 throw new SQLException (Translate.get("backend.null.connection.manager", 805 new String []{name, url})); 806 Iterator iter = connectionManagers.values().iterator(); 807 while (iter.hasNext()) 808 { 809 if (!((AbstractConnectionManager) iter.next()).isInitialized()) 810 return false; 811 } 812 return true; 813 } 814 815 821 public synchronized boolean isJDBCConnected() 822 { 823 try 824 { 825 if (connectionManagers.isEmpty()) 826 throw new SQLException (Translate.get("backend.null.connection.manager", 827 new String []{name, url})); 828 829 AbstractConnectionManager connectionManager; 830 Iterator iter = connectionManagers.values().iterator(); 831 connectionManager = (AbstractConnectionManager) iter.next(); 832 833 Connection con = connectionManager.getConnectionFromDriver(); 834 con.createStatement().execute(this.connectionTestStatement); 835 return true; 836 } 837 catch (Exception e) 838 { 839 String msg = Translate.get("loadbalancer.backend.unreacheable", name); 840 logger.warn(msg, e); 841 return false; 842 } 843 } 844 845 850 public synchronized boolean isReadEnabled() 851 { 852 return state == BackendState.READ_ENABLED_WRITE_DISABLED 853 || state == BackendState.READ_ENABLED_WRITE_ENABLED; 854 } 855 856 861 public synchronized boolean isWriteEnabled() 862 { 863 return state == BackendState.READ_ENABLED_WRITE_ENABLED 864 || state == BackendState.READ_DISABLED_WRITE_ENABL
|