fix #8171 internally caching new connection in WrappedCdmDataSource to avoid connecti...
[cdmlib.git] / cdmlib-persistence / src / main / java / eu / etaxonomy / cdm / database / WrappedCdmDataSource.java
1 /**
2 *
3 */
4 package eu.etaxonomy.cdm.database;
5
6 import java.io.PrintWriter;
7 import java.net.URISyntaxException;
8 import java.sql.Connection;
9 import java.sql.DatabaseMetaData;
10 import java.sql.PreparedStatement;
11 import java.sql.ResultSet;
12 import java.sql.SQLException;
13 import java.sql.SQLFeatureNotSupportedException;
14 import java.util.Map;
15
16 import javax.sql.DataSource;
17
18 import org.apache.log4j.Logger;
19 import org.hibernate.cache.spi.RegionFactory;
20 import org.springframework.beans.factory.config.BeanDefinition;
21
22 import com.mchange.v2.c3p0.ComboPooledDataSource;
23
24 import eu.etaxonomy.cdm.config.CdmSourceException;
25 import eu.etaxonomy.cdm.model.metadata.CdmMetaDataPropertyName;
26 import eu.etaxonomy.cdm.persistence.hibernate.HibernateConfiguration;
27
28 /**
29 * This class is a wrapper class to wrap an {@link javax.sql.DataSource} to an
30 * {@link ICdmDataSource}. As the former is a very limited interface it is not possible
31 * to implement all methods of {@link ICdmDataSource}. However, the aim is
32 * to implement all those methods which are usually needed to work with a datasource
33 * which represents a connection to a database such as transaction handling and
34 * sending queries.
35 * Those methods which are not supported by this wrapper class will throw an xxx
36 * exception.
37 *
38 *
39 * @author a.mueller
40 */
41
42 //FIXME this class replicates lots of code in CdmDataSourceBase, we may want to merge it
43 //in a common helper class to avoid redundant code
44 public class WrappedCdmDataSource implements ICdmDataSource {
45 private static final Logger logger = Logger.getLogger(WrappedCdmDataSource.class);
46
47
48 private final DataSource datasource;
49
50 private Connection connection;
51
52
53 public WrappedCdmDataSource(DataSource datasource) {
54 if (datasource == null){
55 throw new NullPointerException("datasource must not be null for WrappedCdmDataSource");
56 }
57 this.datasource = datasource;
58 }
59
60 /**
61 * {@inheritDoc}
62 */
63 @Override
64 public Connection getConnection() throws SQLException {
65 if (this.connection == null){
66 this.connection = datasource.getConnection();
67 }
68 return this.connection;
69 }
70
71 @Override
72 public Connection getConnection(String username, String password) throws SQLException {
73 if (this.connection == null){
74 this.connection = datasource.getConnection(username, password);
75 }
76 return connection;
77 }
78
79 @Override
80 public PrintWriter getLogWriter() throws SQLException {
81 return datasource.getLogWriter();
82 }
83
84 @Override
85 public void setLogWriter(PrintWriter out) throws SQLException {
86 datasource.setLogWriter(out);
87 }
88
89 @Override
90 public void setLoginTimeout(int seconds) throws SQLException {
91 datasource.setLoginTimeout(seconds);
92 }
93
94 @Override
95 public int getLoginTimeout() throws SQLException {
96 return datasource.getLoginTimeout();
97 }
98
99 @Override
100 public java.util.logging.Logger getParentLogger() throws SQLFeatureNotSupportedException {
101 return datasource.getParentLogger();
102 }
103
104 @Override
105 public <T> T unwrap(Class<T> iface) throws SQLException {
106 return datasource.unwrap(iface);
107 }
108
109 @Override
110 public boolean isWrapperFor(Class<?> iface) throws SQLException {
111 return datasource.isWrapperFor(iface);
112 }
113
114 @Override
115 public String getName() {
116 throw new UnsupportedOperationException("getName() not supported by WrappedCdmDataSource");
117 }
118
119 @Override
120 public void setName(String name) {
121 throw new UnsupportedOperationException("setName(String) not supported by WrappedCdmDataSource");
122 }
123
124 @Override
125 public String getServer() {
126 //TODO we may want to use client info from connection here
127 throw new UnsupportedOperationException("getServer() not supported by WrappedCdmDataSource");
128 }
129
130 @Override
131 public void setServer(String server) {
132 throw new UnsupportedOperationException("setServer() not supported by WrappedCdmDataSource");
133 }
134
135 @Override
136 public int getPort() {
137 //TODO we may want to use client info from connection here
138 throw new UnsupportedOperationException("getPort() not supported by WrappedCdmDataSource");
139 }
140
141 @Override
142 public void setPort(int port) {
143 throw new UnsupportedOperationException("setPort(int) not supported by WrappedCdmDataSource");
144 }
145
146 @Override
147 public String getDbSchemaVersion() throws CdmSourceException {
148 try {
149 return (String)getSingleValue(CdmMetaDataPropertyName.DB_SCHEMA_VERSION.getSqlQuery());
150 } catch (SQLException e1) {
151 try {
152 return (String)getSingleValue(CdmMetaDataPropertyName.DB_SCHEMA_VERSION.getSqlQueryOld());
153 } catch (SQLException e) {
154 throw new CdmSourceException(e.getMessage());
155 }
156 }
157 }
158
159
160 @Override
161 public boolean isDbEmpty() throws CdmSourceException {
162 // Any CDM DB should have a schema version
163 String dbSchemaVersion = getDbSchemaVersion();
164 return (dbSchemaVersion == null || dbSchemaVersion.equals(""));
165 }
166
167 @Override
168 public boolean checkConnection() throws CdmSourceException {
169 try {
170 return testConnection();
171 } catch (ClassNotFoundException e) {
172 throw new CdmSourceException(e.getMessage());
173 } catch (SQLException e) {
174 throw new CdmSourceException(e.getMessage());
175 }
176 }
177
178 @Override
179 public String getConnectionMessage() {
180 try {
181 Connection connection = getConnection();
182 String message = "Connecting to datasource " + connection.getSchema() + ".";
183 return message;
184 } catch (SQLException e) {
185 throw new RuntimeException(e);
186 }
187 }
188
189 @Override
190 public void closeOpenConnections() {
191 try {
192 if(connection != null && !connection.isClosed()){
193 connection.close();
194 connection = null;
195 }
196 } catch (SQLException e) {
197 logger.error("Error closing the connection");
198 }
199 }
200
201 @Override
202 public Map<CdmMetaDataPropertyName, String> getMetaDataMap() throws CdmSourceException {
203 //TODO is it possible/required to build a meta data map here?
204 throw new UnsupportedOperationException("getMetaDataMap() not supported by WrappedCdmDataSource");
205 }
206
207 @Override
208 public BeanDefinition getDatasourceBean() {
209 //TODO is it possible/required to build a datasource bean here?
210 throw new UnsupportedOperationException("getDatasourceBean() not supported by WrappedCdmDataSource");
211 }
212
213 @Override
214 public BeanDefinition getHibernatePropertiesBean(DbSchemaValidation hbm2dll) {
215 //TODO is it possible/required to build a properties bean here?
216 throw new UnsupportedOperationException("getHibernatePropertiesBean() not supported by WrappedCdmDataSource");
217 }
218
219 @Override
220 @Deprecated
221 public BeanDefinition getHibernatePropertiesBean(DbSchemaValidation hbm2dll,
222 Boolean showSql, Boolean formatSql, Boolean registerSearchListener,
223 Class<? extends RegionFactory> cacheProviderClass) {
224 //TODO is it possible/required to build a properties bean here?
225 throw new UnsupportedOperationException("getHibernatePropertiesBean() not supported by WrappedCdmDataSource");
226 }
227
228 /**
229 * {@inheritDoc}
230 */
231 @Override
232 public BeanDefinition getHibernatePropertiesBean(DbSchemaValidation hbm2dll,
233 HibernateConfiguration hibernateConfig) {
234 //TODO is it possible/required to build a properties bean here?
235 throw new UnsupportedOperationException("getHibernatePropertiesBean() not supported by WrappedCdmDataSource");
236 }
237
238 @Override
239 public String getFilePath() {
240 throw new UnsupportedOperationException("getFilePath() not supported by WrappedCdmDataSource");
241 }
242
243 @Override
244 public H2Mode getMode() {
245 throw new UnsupportedOperationException("getMode() not supported by WrappedCdmDataSource");
246 }
247
248 @Override
249 public String getUsername() {
250 //TODO maybe this can be implemented by connection meta data
251 throw new UnsupportedOperationException("getUsername() not supported by WrappedCdmDataSource");
252 }
253
254 @Override
255 public String getPassword() {
256 throw new UnsupportedOperationException("getPassword() not supported by WrappedCdmDataSource");
257 }
258
259 @Override
260 public String getDatabase() {
261 if(datasource instanceof ComboPooledDataSource) {
262 String jdbcUrl = ((ComboPooledDataSource)datasource).getJdbcUrl();
263 try {
264 return getDatabaseFrom(jdbcUrl);
265 } catch (URISyntaxException e) {
266 throw new RuntimeException(e);
267 }
268 } else {
269 throw new UnsupportedOperationException("getDatabase() not implemented for " + datasource.getClass() + " in WrappedCdmDataSource");
270 }
271 }
272
273 /**
274 * @param dbType
275 * @param jdbcUrl
276 * @return
277 * @throws URISyntaxException
278 */
279 private String getDatabaseFrom(String jdbcUrl) throws URISyntaxException {
280 DatabaseTypeEnum type = DatabaseTypeEnum.byConnectionString(jdbcUrl);
281 if (type == null){
282 return null;
283 }else{
284 String dbName = type.getDatabaseType().getDatabaseNameByConnectionString(jdbcUrl);
285 return dbName;
286 }
287 }
288
289 @Override
290 public void setMode(H2Mode h2Mode) {
291 throw new UnsupportedOperationException("setMode(H2Mode) not supported by WrappedCdmDataSource");
292 }
293
294 @Override
295 public void setUsername(String username) {
296 throw new UnsupportedOperationException("setUsername(String) not supported by WrappedCdmDataSource");
297 }
298
299 @Override
300 public void setPassword(String password) {
301 throw new UnsupportedOperationException("setPassword(String) not supported by WrappedCdmDataSource");
302 }
303
304 @Override
305 public void setDatabase(String database) {
306 throw new UnsupportedOperationException("setDatabase(String) not supported by WrappedCdmDataSource");
307 }
308
309 @Override
310 public DatabaseTypeEnum getDatabaseType() {
311 if (this.datasource instanceof ICdmDataSource){
312 return ((ICdmDataSource)this.datasource).getDatabaseType();
313 }
314
315 try {
316 getConnection();
317 } catch (SQLException e1) {
318 throw new RuntimeException("SQL Exception while trying to establish connection to datasource");
319 }
320
321 String driverName = null;
322 if (connection != null){
323 DatabaseMetaData metaData = null;
324 try {
325 metaData = connection.getMetaData();
326 } catch (SQLException e) {
327 throw new RuntimeException("SQL Exception while trying to read datasource metadata");
328 }
329
330 try {
331 driverName = metaData != null ? metaData.getDriverName() : null;
332 } catch (SQLException e) {
333 //throw exception at end
334 }
335 if (metaData != null){
336 DatabaseTypeEnum type = DatabaseTypeEnum.byDatabaseMetaData(metaData);
337 if (type != null){
338 return type;
339 }
340 }
341 throw new IllegalStateException("datasource type (MySQL, SQL Server, ...) could not be retrieved from generic datasource");
342
343 }
344 throw new IllegalStateException("datasource type (MySQL, SQL Server, ...) could not be retrieved from generic datasource");
345 }
346
347 @Override
348 public boolean testConnection() throws ClassNotFoundException, SQLException {
349 return getConnection() != null;
350 }
351
352 @Override
353 public ResultSet executeQuery(String query) throws SQLException {
354 PreparedStatement a = getConnection().prepareStatement(query);
355 return a.executeQuery();
356 }
357
358 @Override
359 public int executeUpdate(String sqlUpdate) throws SQLException {
360 PreparedStatement a = getConnection().prepareStatement(sqlUpdate);
361 return a.executeUpdate();
362 }
363
364 @Override
365 public void startTransaction() {
366 try {
367 Connection connection = getConnection();
368 this.connection = connection;
369 connection.setAutoCommit(false);
370 } catch (SQLException e) {
371 throw new RuntimeException(e);
372 }
373 }
374
375 @Override
376 public void commitTransaction() throws SQLException {
377 getConnection().commit();
378 }
379
380 @Override
381 public void rollback() throws SQLException {
382 getConnection().rollback();
383 }
384
385 @Override
386 public Object getSingleValue(String query) throws SQLException {
387 ResultSet rs = this.executeQuery(query);
388 if (rs.next()){
389 int count = rs.getMetaData().getColumnCount();
390 if (count > 0){
391 return rs.getObject(1);
392 }
393 }
394 return null;
395 }
396
397 @Override
398 public DatabaseMetaData getMetaData() {
399 try {
400 return getConnection().getMetaData();
401 } catch (SQLException e) {
402 throw new RuntimeException(e);
403 }
404 }
405
406 }