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