001package org.javasimon.jdbc4; 002 003import java.sql.Connection; 004import java.sql.DriverManager; 005import java.sql.DriverPropertyInfo; 006import java.sql.SQLException; 007import java.sql.SQLFeatureNotSupportedException; 008import java.util.Properties; 009import java.util.logging.Logger; 010 011/** 012 * Simon JDBC 4.1 Proxy Driver (Java SE 7). 013 * <p> 014 * An application should not use this class directly. The application (if standalone) 015 * should use {@link java.sql.DriverManager} only. For example: 016 * </p> 017 * <pre> 018 * Connection conn = DriverManager.getConnection("jdbc:simon:oracle:thin:...", "scott", "tiger");</pre> 019 * 020 * Simon driver has following format of JDBC connection string: 021 * <pre>{@code 022 * jdbc:simon:<real driver conn string>;<param1>=<value1>;...}</pre> 023 * Simon driver recognizes two parameters: 024 * <ul> 025 * <li> 026 * {@code SIMON_REAL_DRV} - if you don't want or can't register real driver for any 027 * reason, you can use this parameter and Simon proxy driver will do the registration 028 * for you. You don't need to specify real driver parameter for some well known databases. 029 * Simon proxy driver recognize database by first key word after JDBC and register. 030 * </li> 031 * <li> 032 * {@code SIMON_PREFIX} - setting this parameter you can choose different prefix 033 * for all monitors for one instance of driver. For example, setting 034 * {@code SIMON_PREFIX=com.foo} will ensure that all proxy related Simons are located 035 * under the subtree specified by the prefix, e.g. {@code com.foo.conn}, <code>com.foo.stmt</code>, 036 * <code>com.foo.select</code>, etc. If no prefix is set, default {@code org.javasimon.jdbc} prefix 037 * is used. 038 * </li> 039 * </ul> ` 040 * 041 * By default, there is no need to load any driver explicitly, because drivers are loaded automatically 042 * (since JDK 1.5) if they are in class path and jar have appropriate 043 * meta information (see {@link java.sql.DriverManager}). 044 * 045 * If this is not a case for any reason, you need to register Simon proxy driver at least. 046 * For real driver Simon proxy driver contains following procedure for find and register it: 047 * <ol> 048 * <li>Simon proxy driver tries if there is registered driver for driver key word. 049 * <li>If not, driver tries if there is real driver parameter in info properties and then registers it. 050 * <li>If not, driver tries to find driver by key word within internal list of well known drivers and 051 * then registers it. For now, list contains default drivers for Oracle, PostgreSQL, Enterprise DB, H2, 052 * MySQL. 053 * <li>If not, driver tries to find real driver param within connection string and then registers it. 054 * <li>If not, getting new connection fails. 055 * </ol> 056 * The safest way to get Simon proxy driver work is to load the drivers, the real one (i.e. oracle) 057 * and a Simon proxy driver explicitly. This can be done using Class.forName. To load the driver and open a 058 * database connection, use following code: 059 * <pre> 060 * Class.forName("oracle.jdbc.driver.OracleDriver"); // loads real driver 061 * Class.forName("org.javasimon.jdbc4.Driver"); // loads Simon proxy driver 062 * Connection conn = DriverManager.getConnection( 063 * "jdbc:simon:oracle:thin:...", "scott", "tiger");</pre> 064 * 065 * @author Radovan Sninsky 066 * @author <a href="mailto:virgo47@gmail.com">Richard "Virgo" Richter</a> 067 * @see java.sql.DriverManager#getConnection(String) 068 * @since 2.4 069 */ 070public final class Driver implements java.sql.Driver { 071 static { 072 try { 073 DriverManager.registerDriver(new Driver()); 074 } catch (Exception e) { 075 // don't know what to do yet, maybe throw RuntimeException ??? 076 e.printStackTrace(); 077 } 078 } 079 080 private java.sql.Driver driver; 081 082 /** 083 * Class constructor. It loads well known driver list from resource file drivers.properties. 084 */ 085 public Driver() { 086 } 087 088 /** 089 * Opens new Simon proxy driver connection associated with real connection to the specified database. 090 * 091 * @param simonUrl JDBC connection string (i.e. jdbc:simon:h2:file:test) 092 * @param info properties for connection 093 * @return open connection to database or null if provided url is not accepted by this driver 094 * @throws java.sql.SQLException if there is no real driver registered/recognized or opening real connection fails 095 * @see Driver 096 */ 097 @Override 098 public Connection connect(String simonUrl, Properties info) throws SQLException { 099 if (!acceptsURL(simonUrl)) { 100 return null; 101 } 102 103 SimonConnectionConfiguration url = new SimonConnectionConfiguration(simonUrl); 104 driver = getRealDriver(url, info); 105 106 return new SimonConnection(driver.connect(url.getRealUrl(), info), url.getPrefix()); 107 } 108 109 /** 110 * Tries to determine driver class, instantiate it and register if already not registered. 111 * For more detail look at {@link Driver} class javadoc. 112 * 113 * @param configuration instance of url object that represents url 114 * @param info parameters from {@link #connect(String, java.util.Properties)} method 115 * @return instance of real driver 116 * @throws java.sql.SQLException if real driver can't be determined or is not registerd 117 */ 118 private java.sql.Driver getRealDriver(SimonConnectionConfiguration configuration, Properties info) throws SQLException { 119 java.sql.Driver drv = null; 120 try { 121 drv = DriverManager.getDriver(configuration.getRealUrl()); 122 } catch (SQLException e) { 123 // nothing, not an error 124 } 125 126 if (drv == null && info != null && info.keySet().contains(SimonConnectionConfiguration.REAL_DRIVER)) { 127 drv = registerDriver(info.getProperty(SimonConnectionConfiguration.REAL_DRIVER)); 128 } 129 130 if (drv == null && configuration.getRealDriver() != null) { 131 drv = registerDriver(configuration.getRealDriver()); 132 } 133 134 if (drv == null) { 135 if (configuration.getRealDriver() != null) { 136 drv = registerDriver(configuration.getRealDriver()); 137 } 138 } 139 140 if (drv == null) { 141 throw new SQLException("Real driver is not registered and can't determine real driver class name for registration."); 142 } 143 return drv; 144 } 145 146 /** 147 * Registers real driver through {@link java.sql.DriverManager}. 148 * 149 * @param name real driver class name 150 * @return instance of registered real driver 151 * @throws java.sql.SQLException if registration fails 152 */ 153 private java.sql.Driver registerDriver(String name) throws SQLException { 154 try { 155 java.sql.Driver d = (java.sql.Driver) Class.forName(name).newInstance(); 156 DriverManager.registerDriver(d); 157 return d; 158 } catch (SQLException e) { 159 throw e; 160 } catch (Exception e) { 161 return null; 162 } 163 } 164 165 @Override 166 public boolean acceptsURL(String url) throws SQLException { 167 return SimonConnectionConfiguration.isSimonUrl(url); 168 } 169 170 @Override 171 public DriverPropertyInfo[] getPropertyInfo(String s, Properties properties) throws SQLException { 172 return new DriverPropertyInfo[0]; 173 } 174 175 @Override 176 public int getMajorVersion() { 177 return 2; 178 } 179 180 @Override 181 public int getMinorVersion() { 182 return 4; 183 } 184 185 @Override 186 public boolean jdbcCompliant() { 187 return true; 188 } 189 190 @Override 191 public Logger getParentLogger() throws SQLFeatureNotSupportedException { 192 return driver.getParentLogger(); 193 } 194}