1 25 26 package org.objectweb.cjdbc.driver; 27 28 import java.net.Socket ; 29 import java.sql.DriverPropertyInfo ; 30 import java.sql.SQLException ; 31 import java.util.ArrayList ; 32 import java.util.HashMap ; 33 import java.util.Iterator ; 34 import java.util.Properties ; 35 36 import javax.net.SocketFactory; 37 38 import org.objectweb.cjdbc.common.exceptions.AuthenticationException; 39 import org.objectweb.cjdbc.common.exceptions.NoMoreControllerException; 40 import org.objectweb.cjdbc.common.exceptions.driver.DriverSQLException; 41 import org.objectweb.cjdbc.common.net.SSLConfiguration; 42 import org.objectweb.cjdbc.common.net.SocketFactoryFactory; 43 import org.objectweb.cjdbc.common.stream.CJDBCInputStream; 44 import org.objectweb.cjdbc.common.stream.CJDBCOutputStream; 45 import org.objectweb.cjdbc.common.util.Constants; 46 import org.objectweb.cjdbc.controller.core.ControllerConstants; 47 import org.objectweb.cjdbc.driver.protocol.Commands; 48 49 135 136 public class Driver implements java.sql.Driver 137 { 138 139 public static final int MAJOR_VERSION = Constants 140 .getMajorVersion(); 141 142 143 public static final int MINOR_VERSION = Constants 144 .getMinorVersion(); 145 146 protected static final boolean SSL_ENABLED_PROPERTY = "true" 147 .equalsIgnoreCase(System 148 .getProperty("cjdbc.ssl.enabled")); 149 150 154 public static final long DEFAULT_RETRY_INTERVAL_IN_MS = 5000; 155 156 161 protected static ArrayList driverProperties; 162 163 164 protected static final String HOST_PROPERTY = "HOST"; 165 protected static final String PORT_PROPERTY = "PORT"; 166 protected static final String DATABASE_PROPERTY = "DATABASE"; 167 protected static final String USER_PROPERTY = "user"; 168 protected static final String PASSWORD_PROPERTY = "password"; 169 protected static final String BOOLEAN_TRUE_PROPERTY = "booleanTrue"; 170 protected static final String BOOLEAN_FALSE_PROPERTY = "booleanFalse"; 171 protected static final String ESCAPE_BACKSLASH_PROPERTY = "escapeBackslash"; 172 protected static final String ESCAPE_SINGLE_QUOTE_PROPERTY = "escapeSingleQuote"; 173 protected static final String ESCAPE_CHARACTER_PROPERTY = "escapeCharacter"; 174 protected static final String DRIVER_PROCESSED_PROPERTY = "driverProcessed"; 175 protected static final String CONNECTION_POOLING_PROPERTY = "connectionPooling"; 176 protected static final String PREFERRED_CONTROLLER_PROPERTY = "preferredController"; 177 protected static final String RETRY_INTERVAL_IN_MS_PROPERTY = "retryIntervalInMs"; 178 protected static final String DEBUG_PROPERTY = "debugLevel"; 179 180 181 private static final String HOST_PROPERTY_DESCRIPTION = "Hostname of C-JDBC controller"; 182 private static final String PORT_PROPERTY_DESCRIPTION = "Port number of C-JDBC controller"; 183 private static final String DATABASE_PROPERTY_DESCRIPTION = "Database name"; 184 private static final String USER_PROPERTY_DESCRIPTION = "Username to authenticate as"; 185 private static final String PASSWORD_PROPERTY_DESCRIPTION = "Password to use for authentication"; 186 private static final String BOOLEAN_TRUE_PROPERTY_DESCRIPTION = "Use this value for the 'true' value when using PreparedStatement.setBoolean method"; 187 private static final String BOOLEAN_FALSE_PROPERTY_DESCRIPTION = "Use this value for the 'false' value when using PreparedStatement.setBoolean method"; 188 private static final String ESCAPE_BACKSLASH_PROPERTY_DESCRIPTION = "Set this to true to escape backslashes when performing escape processing of PreparedStatements"; 189 private static final String ESCAPE_SINGLE_QUOTE_PROPERTY_DESCRIPTION = "Set this to true to escape single quotes (') when performing escape processing of PreparedStatements"; 190 private static final String ESCAPE_CHARACTER_PROPERTY_DESCRIPTION = "Use this character to prepend and append to the values when performing escape processing of PreparedStatements"; 191 private static final String DRIVER_PROCESSED_PROPERTY_DESCRIPTION = "Set this to false to let queries be passed and prepared for each individual backend"; 192 protected static final String CONNECTION_POOLING_PROPERTY_DESCRIPTION = "Set this to false if you do not want the driver to perform transparent connection pooling"; 193 protected static final String PREFERRED_CONTROLLER_PROPERTY_DESCRIPTION = "Defines the strategy to use to choose a preferred controller to connect to"; 194 protected static final String RETRY_INTERVAL_IN_MS_PROPERTY_DESCRIPTION = "Interval in milliseconds before retrying to re-connect to a controller that has failed"; 195 protected static final String DEBUG_PROPERTY_DESCRIPTION = "Debug level that can be set to 'debug', 'info' or 'off'"; 196 197 198 public static final String CJDBC_URL_HEADER = "jdbc:cjdbc://"; 199 200 201 public static final int CJDBC_URL_HEADER_LENGTH = CJDBC_URL_HEADER 202 .length(); 203 204 211 private HashMap parsedUrlsCache = new HashMap (); 212 213 214 protected ArrayList pendingConnectionClosing = new ArrayList (); 215 protected boolean connectionClosingThreadisAlive = false; 216 217 static 220 { 221 try 224 { 225 java.sql.DriverManager.registerDriver(new Driver()); 226 } 227 catch (SQLException e) 228 { 229 throw new RuntimeException ("Unable to register C-JDBC driver"); 230 } 231 232 driverProperties = new ArrayList (); 234 driverProperties.add(Driver.HOST_PROPERTY); 235 driverProperties.add(Driver.PORT_PROPERTY); 236 driverProperties.add(Driver.DATABASE_PROPERTY); 237 driverProperties.add(Driver.USER_PROPERTY); 238 driverProperties.add(Driver.PASSWORD_PROPERTY); 239 driverProperties.add(Driver.BOOLEAN_TRUE_PROPERTY); 240 driverProperties.add(Driver.BOOLEAN_FALSE_PROPERTY); 241 driverProperties.add(Driver.ESCAPE_BACKSLASH_PROPERTY); 242 driverProperties.add(Driver.ESCAPE_SINGLE_QUOTE_PROPERTY); 243 driverProperties.add(Driver.ESCAPE_CHARACTER_PROPERTY); 244 driverProperties.add(Driver.DRIVER_PROCESSED_PROPERTY); 245 driverProperties.add(Driver.CONNECTION_POOLING_PROPERTY); 246 driverProperties.add(Driver.PREFERRED_CONTROLLER_PROPERTY); 247 driverProperties.add(Driver.RETRY_INTERVAL_IN_MS_PROPERTY); 248 driverProperties.add(Driver.DEBUG_PROPERTY); 249 } 250 251 255 public Driver() 256 { 257 } 259 260 274 public java.sql.Connection connect(String url, Properties properties) 275 throws SQLException 276 { 277 if (url == null) 278 throw new SQLException ("Invalid null URL in connect"); 279 280 285 if (!url.startsWith(CJDBC_URL_HEADER)) 286 return null; 287 288 CjdbcUrl cjdbcUrl = (CjdbcUrl) parsedUrlsCache.get(url); 290 if (cjdbcUrl == null) { 292 synchronized (this) 293 { 294 cjdbcUrl = (CjdbcUrl) parsedUrlsCache.get(url); 297 if (cjdbcUrl == null) 298 { 299 cjdbcUrl = new CjdbcUrl(url); 300 parsedUrlsCache.put(url, cjdbcUrl); 301 } 302 } 303 } 304 305 ControllerInfo controller = null; 306 try 307 { 308 controller = cjdbcUrl.getControllerConnectPolicy().getController(); 310 return connectToController(properties, cjdbcUrl, controller); 311 } 312 catch (AuthenticationException e) 313 { 314 throw new SQLException (e.getMessage()); 315 } 316 catch (NoMoreControllerException e) 317 { 318 throw new SQLException ("No controller is available to accept connections"); 319 } 320 catch (SQLException e) 321 { 322 if (controller != null) 324 { 325 cjdbcUrl.getControllerConnectPolicy().suspectControllerOfFailure( 326 controller); 327 System.out.println("retrying"); 329 return connect(url, properties); 330 } 331 else 332 throw e; 333 } 334 catch (RuntimeException e) 335 { 336 e.printStackTrace(); 337 throw new SQLException ( 338 "Unable to connect to the virtual database - Unexpected runtime error (" 339 + e + ")"); 340 } 341 } 342 343 355 protected java.sql.Connection connectToController(Properties properties, 356 CjdbcUrl cjdbcUrl, ControllerInfo controller) throws 357 AuthenticationException, DriverSQLException 358 { 359 String user = null; 361 if (properties != null) 362 user = properties.getProperty(USER_PROPERTY); 363 if (user == null) 364 user = (String ) cjdbcUrl.getParameters().get(USER_PROPERTY); 365 if (user == null || user.equals("")) 366 throw new AuthenticationException("Invalid user name in connect"); 367 368 String password = null; 370 if (properties != null) 371 password = properties.getProperty(PASSWORD_PROPERTY); 372 if (password == null) 373 password = (String ) cjdbcUrl.getParameters().get(PASSWORD_PROPERTY); 374 if (password == null) 375 password = ""; 376 377 if (!"false".equals(cjdbcUrl.getParameters().get( 379 CONNECTION_POOLING_PROPERTY))) 380 { java.sql.Connection c = retrievePendingClosingConnection(properties, 382 cjdbcUrl.getUrl(), controller, user, password); 383 if (c != null) 384 { 385 if (cjdbcUrl.isDebugEnabled()) 386 System.out.println("Reusing connection from pool"); 387 return c; } 389 } 390 391 393 boolean sentVdbName = false; 396 boolean sentUserInfo = false; 397 try 398 { 399 Socket socket = null; 401 402 if (SSL_ENABLED_PROPERTY) 404 { 405 SocketFactory sslFact = SocketFactoryFactory 406 .createFactory(SSLConfiguration.getDefaultConfig()); 407 socket = sslFact.createSocket(controller.getHostname(), controller 408 .getPort()); 409 } 410 else 411 { 412 socket = new Socket (controller.getHostname(), controller.getPort()); 414 } 415 416 socket.setTcpNoDelay(true); 419 420 if (cjdbcUrl.isInfoEnabled()) 421 System.out.println("Authenticating with controller " + controller); 422 423 CJDBCOutputStream out = new CJDBCOutputStream(socket); 424 out.writeInt(Commands.ProtocolVersion); 426 out.writeUTF(cjdbcUrl.getDatabaseName()); 427 out.flush(); 428 sentVdbName = true; 429 430 out.writeUTF(user); 432 out.writeUTF(password); 433 out.flush(); 434 sentUserInfo = true; 435 436 CJDBCInputStream in; 437 Connection con; 438 439 in = new CJDBCInputStream(socket); 441 442 con = new Connection(this, socket, in, out, cjdbcUrl, controller, user, 443 password); 444 445 return setParametersOnConnection(properties, con); 446 447 } catch (Exception re) 449 { 450 if (!sentVdbName) 451 throw new DriverSQLException("Unable to connect to controller on " 452 + controller.getHostname() + ":" + controller.getPort() + " (" + re 453 + ")"); 454 else if (re instanceof AuthenticationException) 455 throw (AuthenticationException) re; 456 else if (!sentUserInfo) 457 throw new AuthenticationException( 458 "Unable to connect to the virtual database (virtual database name is probably not correct)"); 459 else 460 throw new DriverSQLException( 461 "Unable to connect to the virtual database (" 462 + re.getLocalizedMessage() + ")", re); 463 } 464 } 465 466 475 private java.sql.Connection retrievePendingClosingConnection( 476 Properties connectionInfo, String url, ControllerInfo controllerInfo, 477 String user, String password) 478 { 479 try 492 { 493 Connection c; 494 synchronized (pendingConnectionClosing) 495 { 496 c = (Connection) pendingConnectionClosing 498 .remove(pendingConnectionClosing.size() - 1); 499 } 500 if (url.equals(c.getUrl()) 501 && controllerInfo.equals(c.getControllerInfo()) 502 && user.equals(c.getUserName()) && password.equals(c.getPassword())) 503 { c.isClosed = false; 505 return setParametersOnConnection(connectionInfo, c); 506 } 507 else 508 synchronized (pendingConnectionClosing) 510 { 511 pendingConnectionClosing.add(c); 512 } 513 } 514 catch (IndexOutOfBoundsException ignore) 515 { 516 } 518 return null; 519 } 520 521 531 public synchronized boolean acceptsURL(String url) throws SQLException 532 { 533 try 534 { 535 CjdbcUrl cjdbcUrl = (CjdbcUrl) parsedUrlsCache.get(url); 536 if (cjdbcUrl == null) { 538 synchronized (this) 539 { 540 cjdbcUrl = (CjdbcUrl) parsedUrlsCache.get(url); 543 if (cjdbcUrl == null) 544 { 545 cjdbcUrl = new CjdbcUrl(url); parsedUrlsCache.put(url, cjdbcUrl); 548 } 549 } 550 } 551 return true; 552 } 553 catch (SQLException e) 554 { 555 return false; 556 } 557 } 558 559 576 private java.sql.Connection setParametersOnConnection(Properties props, 577 org.objectweb.cjdbc.driver.Connection connection) 578 { 579 if ((props == null) || props.isEmpty()) 580 return connection; 581 582 String booleanTrue = props.getProperty(BOOLEAN_TRUE_PROPERTY); 583 if (booleanTrue != null) 584 connection.setPreparedStatementBooleanTrue(booleanTrue); 585 String booleanFalse = props.getProperty(BOOLEAN_FALSE_PROPERTY); 586 if (booleanFalse != null) 587 connection.setPreparedStatementBooleanFalse(booleanFalse); 588 String escapeBaskslash = props.getProperty(ESCAPE_BACKSLASH_PROPERTY); 589 if (escapeBaskslash != null) 590 connection 591 .setEscapeBackslash(new Boolean (escapeBaskslash).booleanValue()); 592 String escapeQuote = props.getProperty(ESCAPE_SINGLE_QUOTE_PROPERTY); 593 if (escapeQuote != null) 594 connection.setEscapeSingleQuote(new Boolean (escapeQuote).booleanValue()); 595 596 String escapeChar = props.getProperty(ESCAPE_CHARACTER_PROPERTY); 597 if (escapeChar != null) 598 connection.setEscapeChar(escapeChar); 599 600 String driverProcessed = props.getProperty(DRIVER_PROCESSED_PROPERTY); 601 if (driverProcessed != null) 602 connection.setDriverProcessed(Boolean.valueOf(driverProcessed) 603 .booleanValue()); 604 605 String connectionPooling = props.getProperty(CONNECTION_POOLING_PROPERTY); 606 if (connection != null) 607 connection.setConnectionPooling(Boolean.valueOf(connectionPooling) 608 .booleanValue()); 609 610 return connection; 611 } 612 613 621 public String changeDatabaseName(String url, String newDbName) 622 throws SQLException 623 { 624 StringBuffer sb = new StringBuffer (); 625 sb.append(CJDBC_URL_HEADER); 626 CjdbcUrl cjdbcUrl = (CjdbcUrl) parsedUrlsCache.get(url); 627 if (cjdbcUrl == null) 628 { 629 acceptsURL(url); cjdbcUrl = (CjdbcUrl) parsedUrlsCache.get(url); 631 } 632 ControllerInfo[] controllerList = cjdbcUrl.getControllerList(); 633 for (int i = 0; i < controllerList.length; i++) 634 { 635 if (i == 0) 636 sb.append(controllerList[i].toString()); 637 else 638 sb.append("," + controllerList[i].toString()); 639 } 640 sb.append("/" + newDbName); 641 HashMap params = cjdbcUrl.getParameters(); 642 if (params != null) 643 { 644 Iterator paramsKeys = params.keySet().iterator(); 645 String element = null; 646 while (paramsKeys.hasNext()) 647 { 648 if (element == null) 649 sb.append("?"); 650 else 651 sb.append("&"); 652 element = (String ) paramsKeys.next(); 653 sb.append(element + "=" + params.get(paramsKeys)); 654 } 655 } 656 return sb.toString(); 657 } 658 659 692 public DriverPropertyInfo [] getPropertyInfo(String url, Properties info) 693 throws SQLException 694 { 695 if (!acceptsURL(url)) 696 throw new SQLException ("Invalid url " + url); 697 698 CjdbcUrl cjdbcUrl; 699 synchronized (this) 700 { 701 cjdbcUrl = (CjdbcUrl) parsedUrlsCache.get(url); 702 if (cjdbcUrl == null) 703 throw new SQLException ("Error while retrieving URL information"); 704 } 705 HashMap params = cjdbcUrl.getParameters(); 706 707 String host = info.getProperty(HOST_PROPERTY); 708 if (host == null) 709 { 710 ControllerInfo[] controllerList = cjdbcUrl.getControllerList(); 711 for (int i = 0; i < controllerList.length; i++) 712 { 713 ControllerInfo controller = controllerList[i]; 714 if (i == 0) 715 host = controller.toString(); 716 else 717 host += "," + controller.toString(); 718 } 719 } 720 DriverPropertyInfo hostProp = new DriverPropertyInfo (HOST_PROPERTY, host); 721 hostProp.required = true; 722 hostProp.description = HOST_PROPERTY_DESCRIPTION; 723 724 DriverPropertyInfo portProp = new DriverPropertyInfo (PORT_PROPERTY, info 725 .getProperty(PORT_PROPERTY, Integer 726 .toString(ControllerConstants.DEFAULT_PORT))); 727 portProp.required = false; 728 portProp.description = PORT_PROPERTY_DESCRIPTION; 729 730 String database = info.getProperty(DATABASE_PROPERTY); 731 if (database == null) 732 database = cjdbcUrl.getDatabaseName(); 733 DriverPropertyInfo databaseProp = new DriverPropertyInfo (DATABASE_PROPERTY, 734 database); 735 databaseProp.required = true; 736 databaseProp.description = DATABASE_PROPERTY_DESCRIPTION; 737 738 String user = info.getProperty(USER_PROPERTY); 739 if (user == null) 740 user = (String ) params.get(USER_PROPERTY); 741 DriverPropertyInfo userProp = new DriverPropertyInfo (USER_PROPERTY, user); 742 userProp.required = true; 743 userProp.description = USER_PROPERTY_DESCRIPTION; 744 745 String password = info.getProperty(PASSWORD_PROPERTY); 746 if (password == null) 747 password = (String ) params.get(PASSWORD_PROPERTY); 748 DriverPropertyInfo passwordProp = new DriverPropertyInfo (PASSWORD_PROPERTY, 749 password); 750 passwordProp.required = true; 751 passwordProp.description = PASSWORD_PROPERTY_DESCRIPTION; 752 753 String escapeChar = info.getProperty(ESCAPE_CHARACTER_PROPERTY); 754 if (escapeChar == null) 755 escapeChar = (String ) params.get(ESCAPE_CHARACTER_PROPERTY); 756 DriverPropertyInfo escapeCharProp = new DriverPropertyInfo ( 757 ESCAPE_CHARACTER_PROPERTY, escapeChar); 758 escapeCharProp.required = false; 759 escapeCharProp.description = ESCAPE_CHARACTER_PROPERTY_DESCRIPTION; 760 761 String escapeBackslash = info.getProperty(ESCAPE_BACKSLASH_PROPERTY); 762 if (escapeBackslash == null) 763 escapeBackslash = (String ) params.get(ESCAPE_BACKSLASH_PROPERTY); 764 DriverPropertyInfo escapeBackProp = new DriverPropertyInfo ( 765 ESCAPE_BACKSLASH_PROPERTY, escapeBackslash); 766 escapeBackProp.required = false; 767 escapeBackProp.description = ESCAPE_BACKSLASH_PROPERTY_DESCRIPTION; 768 769 String escapeSingleQuote = info.getProperty(ESCAPE_SINGLE_QUOTE_PROPERTY); 770 if (escapeSingleQuote == null) 771 escapeSingleQuote = (String ) params.get(ESCAPE_SINGLE_QUOTE_PROPERTY); 772 DriverPropertyInfo escapeSingleProp = new DriverPropertyInfo ( 773 ESCAPE_SINGLE_QUOTE_PROPERTY, escapeSingleQuote); 774 escapeSingleProp.required = false; 775 escapeSingleProp.description = ESCAPE_SINGLE_QUOTE_PROPERTY_DESCRIPTION; 776 777 String booleanFalse = info.getProperty(BOOLEAN_FALSE_PROPERTY); 778 if (booleanFalse == null) 779 booleanFalse = (String ) params.get(BOOLEAN_FALSE_PROPERTY); 780 DriverPropertyInfo booleanFalseProp = new DriverPropertyInfo ( 781 BOOLEAN_FALSE_PROPERTY, booleanFalse); 782 booleanFalseProp.required = false; 783 booleanFalseProp.description = BOOLEAN_FALSE_PROPERTY_DESCRIPTION; 784 785 String booleanTrue = info.getProperty(BOOLEAN_TRUE_PROPERTY); 786 if (booleanTrue == null) 787 booleanTrue = (String ) params.get(BOOLEAN_TRUE_PROPERTY); 788 DriverPropertyInfo booleanTrueProp = new DriverPropertyInfo ( 789 BOOLEAN_TRUE_PROPERTY, booleanTrue); 790 booleanTrueProp.required = false; 791 booleanTrueProp.description = BOOLEAN_TRUE_PROPERTY_DESCRIPTION; 792 793 String driverProcessed = info.getProperty(DRIVER_PROCESSED_PROPERTY); 794 if (driverProcessed == null) 795 driverProcessed = (String ) params.get(DRIVER_PROCESSED_PROPERTY); 796 DriverPropertyInfo parseQueryProp = new DriverPropertyInfo ( 797 DRIVER_PROCESSED_PROPERTY, driverProcessed); 798 escapeSingleProp.required = false; 799 escapeSingleProp.description = DRIVER_PROCESSED_PROPERTY_DESCRIPTION; 800 801 String connectionPooling = info.getProperty(CONNECTION_POOLING_PROPERTY); 802 if (connectionPooling == null) 803 connectionPooling = (String ) params.get(CONNECTION_POOLING_PROPERTY); 804 DriverPropertyInfo connectionPoolingProp = new DriverPropertyInfo ( 805 CONNECTION_POOLING_PROPERTY, connectionPooling); 806 connectionPoolingProp.required = false; 807 connectionPoolingProp.description = CONNECTION_POOLING_PROPERTY_DESCRIPTION; 808 809 String preferredController = info 810 .getProperty(PREFERRED_CONTROLLER_PROPERTY); 811 if (preferredController == null) 812 preferredController = (String ) params.get(PREFERRED_CONTROLLER_PROPERTY); 813 DriverPropertyInfo preferredControllerProp = new DriverPropertyInfo ( 814 PREFERRED_CONTROLLER_PROPERTY, preferredController); 815 preferredControllerProp.required = false; 816 preferredControllerProp.description = PREFERRED_CONTROLLER_PROPERTY_DESCRIPTION; 817 818 return new DriverPropertyInfo []{hostProp, portProp, databaseProp, userProp, 819 passwordProp, escapeCharProp, escapeBackProp, escapeSingleProp, 820 booleanFalseProp, booleanTrueProp, parseQueryProp, 821 connectionPoolingProp, preferredControllerProp}; 822 } 823 824 829 public int getMajorVersion() 830 { 831 return MAJOR_VERSION; 832 } 833 834 839 public int getMinorVersion() 840 { 841 return MINOR_VERSION; 842 } 843 844 854 public boolean jdbcCompliant() 855 { 856 return false; 857 } 858 859 } 860 | Popular Tags |