001package org.javasimon.jdbcx4; 002 003import java.io.PrintWriter; 004import java.lang.reflect.Method; 005import java.sql.SQLException; 006import java.util.Properties; 007 008import org.javasimon.jdbc4.RegexBasedNormalizerFactory; 009import org.javasimon.jdbc4.SimonConnectionConfiguration; 010import org.javasimon.jdbc4.SqlNormalizerFactory; 011 012/** 013 * SimonCommonDataSource is parent for all three datasource implementation classes. 014 * <p> 015 * It contains getters and setters for basic properties which all three datasource types 016 * needs to implement. 017 * 018 * @author Radovan Sninsky 019 * @author <a href="mailto:virgo47@gmail.com">Richard "Virgo" Richter</a> 020 * @since 2.4 021 */ 022public abstract class AbstractSimonDataSource { 023 protected transient PrintWriter logWriter; 024 protected SimonConnectionConfiguration configuration; 025 private String user; 026 private String password; 027 private int loginTimeout; 028 029 private String realDataSourceClassName; 030 private String prefix; 031 /** 032 * Properties specific to the real datasource 033 */ 034 private Properties properties; 035 036 protected SqlNormalizerFactory sqlNormalizerFactory = new RegexBasedNormalizerFactory(); 037 038 public void setSqlNormalizerFactory(SqlNormalizerFactory sqlNormalizerFactory) { 039 this.sqlNormalizerFactory = sqlNormalizerFactory; 040 } 041 042 /** 043 * Retrieves the log writer for this <code>DataSource</code> object. 044 * 045 * @return the log writer for this data source or null if logging is disabled 046 * @throws java.sql.SQLException if a database access error occurs 047 * @see javax.sql.DataSource#getLogWriter() 048 * @see #setLogWriter(java.io.PrintWriter) 049 */ 050 public final PrintWriter getLogWriter() throws SQLException { 051 return logWriter; 052 } 053 054 /** 055 * Sets the log writer for this <code>DataSource</code> object to the given <code>java.io.PrintWriter</code> object. 056 * 057 * @param out the new log writer; to disable logging, set to null 058 * @throws java.sql.SQLException if a database access error occurs 059 * @see javax.sql.DataSource#setLogWriter(java.io.PrintWriter) 060 * @see #getLogWriter 061 */ 062 public final void setLogWriter(PrintWriter out) throws SQLException { 063 this.logWriter = out; 064 } 065 066 /** 067 * Returns JDBC connection URL. 068 * 069 * @return JDBC connection URL 070 */ 071 public final String getUrl() { 072 return configuration == null ? null : configuration.getSimonUrl(); 073 } 074 075 /** 076 * Setter for URL property. 077 * 078 * @param url JDBC connection URL 079 */ 080 public final void setUrl(String url) { 081 this.configuration = new SimonConnectionConfiguration(url); 082 } 083 084 /** 085 * Returns real JDBC connection URL. 086 * 087 * @return real JDBC connection URL 088 */ 089 public final String getRealUrl() { 090 return configuration == null ? null : configuration.getRealUrl(); 091 } 092 093 /** 094 * Returns database user to authenticate connection. 095 * 096 * @return database user 097 */ 098 public final String getUser() { 099 return user; 100 } 101 102 /** 103 * Setter for user property. 104 * 105 * @param user database user 106 */ 107 public final void setUser(String user) { 108 this.user = user; 109 } 110 111 /** 112 * Returns database password to authenticate connection. 113 * 114 * @return database password 115 */ 116 public final String getPassword() { 117 return password; 118 } 119 120 /** 121 * Setter for password property. 122 * 123 * @param password database password 124 */ 125 public final void setPassword(String password) { 126 this.password = password; 127 } 128 129 /** 130 * Sets the maximum time in seconds that this data source will wait 131 * while attempting to connect to a database. A value of zero 132 * specifies that the timeout is the default system timeout 133 * if there is one; otherwise, it specifies that there is no timeout. 134 * When a {@code DataSource} object is created, the login timeout is 135 * initially zero. 136 * 137 * @param seconds the data source login time limit 138 * @throws java.sql.SQLException if a database access error occurs. 139 * @see #getLoginTimeout 140 */ 141 public final void setLoginTimeout(int seconds) throws SQLException { 142 this.loginTimeout = seconds; 143 } 144 145 /** 146 * Gets the maximum time in seconds that this data source can wait 147 * while attempting to connect to a database. A value of zero 148 * means that the timeout is the default system timeout 149 * if there is one; otherwise, it means that there is no timeout. 150 * When a {@code DataSource} object is created, the login timeout is 151 * initially zero. 152 * 153 * @return the data source login time limit 154 * @throws java.sql.SQLException if a database access error occurs. 155 * @see #setLoginTimeout 156 */ 157 public final int getLoginTimeout() throws SQLException { 158 return loginTimeout; 159 } 160 161 /** 162 * Returns real datasource class name. 163 * 164 * @return real datasource class name 165 */ 166 public final String getRealDataSourceClassName() { 167 if ((realDataSourceClassName == null || realDataSourceClassName.isEmpty()) && (configuration != null)) { 168 realDataSourceClassName = doGetRealDataSourceClassName(); 169 } 170 return realDataSourceClassName; 171 } 172 173 /** 174 * Setter for realDataSourceClassName property. 175 * 176 * @param className class name of real datasource 177 */ 178 public final void setRealDataSourceClassName(String className) { 179 this.realDataSourceClassName = className; 180 } 181 182 /** 183 * Instantiates the DataSource. 184 * 185 * @param dataSourceClass Expected DataSource class 186 * @param <T> DataSource type 187 * @return Create DataSource 188 * @throws SQLException 189 */ 190 protected final <T> T createDataSource(Class<T> dataSourceClass) throws SQLException { 191 if (getRealDataSourceClassName() == null) { 192 throw new SQLException("Property realDataSourceClassName is not set"); 193 } 194 try { 195 T ds = dataSourceClass.cast(Class.forName(realDataSourceClassName).newInstance()); 196 for (Method m : ds.getClass().getMethods()) { 197 String methodName = m.getName(); 198 if (methodName.startsWith("set") && m.getParameterTypes().length == 1) { 199 final Object propertyValue; 200 if (methodName.equals("setUser")) { 201 propertyValue = getUser(); 202 } else if (methodName.equals("setPassword")) { 203 propertyValue = getPassword(); 204 } else if (methodName.equalsIgnoreCase("setUrl")) { 205 propertyValue = getRealUrl(); 206 } else if (methodName.equals("setLogWriter")) { 207 propertyValue = getLogWriter(); 208 } else if (methodName.equals("setLoginTimeout")) { 209 propertyValue = getLoginTimeout(); 210 } else { 211 String propertyName = methodName.substring(3, 4).toLowerCase(); 212 if (methodName.length() > 4) { 213 propertyName += methodName.substring(4); 214 } 215 final Class<?> propertyType = m.getParameterTypes()[0]; 216 propertyValue = getPropertyAs(propertyName, propertyType); 217 } 218 if (propertyValue != null) { 219 m.invoke(ds, propertyValue); 220 } 221 } 222 } 223 return ds; 224 225 } catch (Exception e) { 226 throw new SQLException(e); 227 } 228 } 229 230 /** 231 * Reads DataSource class name from configuration. 232 */ 233 protected abstract String doGetRealDataSourceClassName(); 234 235 /** 236 * Returns Simon prefix for constructing names of Simons. 237 * 238 * @return Simon prefix 239 */ 240 public final String getPrefix() { 241 if ((prefix == null || prefix.isEmpty()) && (configuration != null)) { 242 prefix = configuration.getPrefix(); 243 } 244 return prefix; 245 } 246 247 /** 248 * Sets Simon prefix for constructing names of Simons. 249 * 250 * @param prefix Simon prefix 251 */ 252 public final void setPrefix(String prefix) { 253 this.prefix = prefix; 254 } 255 256 /** 257 * Get properties specific to the real datasource. 258 * 259 * @return Properties 260 */ 261 public Properties getProperties() { 262 return properties; 263 } 264 265 /** 266 * Set properties specific to the real datasource. 267 */ 268 public void setProperties(Properties properties) { 269 this.properties = properties; 270 } 271 272 /** 273 * Returns property from {@link #properties} and convert it to given type. 274 * 275 * @param <T> property type 276 * @param propertyName property name 277 * @param propertyType property type 278 * @return property value 279 */ 280 @SuppressWarnings("unchecked") 281 private <T> T getPropertyAs(String propertyName, Class<T> propertyType) { 282 final String sValue = properties == null ? null : properties.getProperty(propertyName); 283 final T value; 284 if (sValue == null) { 285 value = null; 286 } else if (propertyType.equals(String.class)) { 287 value = propertyType.cast(sValue); 288 } else if (propertyType.equals(Integer.class)) { 289 value = propertyType.cast(Integer.valueOf(sValue)); 290 } else if (propertyType.equals(int.class)) { 291 value = (T) Integer.valueOf(sValue); 292 } else if (propertyType.equals(Long.class)) { 293 value = propertyType.cast(Long.valueOf(sValue)); 294 } else if (propertyType.equals(long.class)) { 295 value = (T) Long.valueOf(sValue); 296 } else if (propertyType.equals(Boolean.class)) { 297 value = propertyType.cast(Boolean.valueOf(sValue)); 298 } else if (propertyType.equals(boolean.class)) { 299 value = (T) Boolean.valueOf(sValue); 300 } else { 301 value = null; 302 } 303 return value; 304 } 305}