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}