further fixes for subareas #4220
[cdmlib.git] / cdmlib-services / src / main / java / eu / etaxonomy / cdm / api / service / DatabaseServiceHibernateImpl.java
1 // $Id$
2 /**
3 * Copyright (C) 2007 EDIT
4 * European Distributed Institute of Taxonomy
5 * http://www.e-taxonomy.eu
6 *
7 * The contents of this file are subject to the Mozilla Public License Version 1.1
8 * See LICENSE.TXT at the top of this package for the full license terms.
9 */
10
11 package eu.etaxonomy.cdm.api.service;
12
13 import java.sql.Connection;
14 import java.sql.ResultSet;
15 import java.sql.SQLException;
16 import java.sql.Statement;
17 import java.util.HashMap;
18 import java.util.Map;
19
20 import javax.sql.DataSource;
21
22 import org.apache.log4j.Logger;
23 import org.hibernate.SessionFactory;
24 import org.springframework.beans.BeansException;
25 import org.springframework.beans.factory.annotation.Autowired;
26 import org.springframework.context.ApplicationContext;
27 import org.springframework.context.ApplicationContextAware;
28 import org.springframework.jdbc.datasource.AbstractDriverBasedDataSource;
29 import org.springframework.orm.hibernate4.SessionFactoryUtils;
30 import org.springframework.stereotype.Service;
31 import org.springframework.transaction.annotation.Transactional;
32
33 import eu.etaxonomy.cdm.api.application.CdmApplicationController;
34 import eu.etaxonomy.cdm.config.CdmPersistentSourceUtils;
35 import eu.etaxonomy.cdm.config.CdmSourceException;
36 import eu.etaxonomy.cdm.database.CdmDataSource;
37 import eu.etaxonomy.cdm.database.CdmPersistentDataSource;
38 import eu.etaxonomy.cdm.database.DataSourceNotFoundException;
39 import eu.etaxonomy.cdm.database.DatabaseTypeEnum;
40 import eu.etaxonomy.cdm.database.H2Mode;
41 import eu.etaxonomy.cdm.database.ICdmDataSource;
42 import eu.etaxonomy.cdm.model.common.init.TermNotFoundException;
43 import eu.etaxonomy.cdm.model.metadata.CdmMetaData.MetaDataPropertyName;
44 import eu.etaxonomy.cdm.model.name.NomenclaturalCode;
45
46
47
48 /**
49 * Implementation of service which provides functionality to directly access database
50 * related information.
51 *
52 * @author a.mueller
53 *
54 */
55 @Service
56 @Transactional(readOnly = true)
57 public class DatabaseServiceHibernateImpl implements IDatabaseService, ApplicationContextAware {
58 private static final Logger logger = Logger.getLogger(DatabaseServiceHibernateImpl.class);
59
60 private static final String TMP_DATASOURCE = "tmp";
61
62 @Autowired
63 private SessionFactory factory;
64
65 @Autowired
66 protected ApplicationContext appContext;
67
68 private CdmApplicationController application;
69
70
71 /* (non-Javadoc)
72 * @see eu.etaxonomy.cdm.api.service.IDatabaseService#setApplicationController(eu.etaxonomy.cdm.api.application.CdmApplicationController)
73 */
74 public void setApplicationController(CdmApplicationController cdmApplicationController){
75 this.application = cdmApplicationController;
76 }
77
78
79 /* (non-Javadoc)
80 * @see eu.etaxonomy.cdm.api.service.IDatabaseService#connectToDatasource(eu.etaxonomy.cdm.database.CdmDataSource)
81 */
82 public boolean connectToDatasource(CdmPersistentDataSource dataSource) throws TermNotFoundException{
83 this.application.changeDataSource(dataSource);
84 logger.debug("DataSource changed to " + dataSource.getName());
85 return true;
86 }
87
88 /* (non-Javadoc)
89 * @see eu.etaxonomy.cdm.api.service.IDatabaseService#connectToDatabase(eu.etaxonomy.cdm.database.DatabaseTypeEnum, java.lang.String, java.lang.String, java.lang.String, java.lang.String, int)
90 */
91 public boolean connectToDatabase(DatabaseTypeEnum databaseTypeEnum, String server,
92 String database, String username, String password, int port, String filePath, H2Mode mode, NomenclaturalCode code) throws TermNotFoundException {
93 ICdmDataSource dataSource = CdmDataSource.NewInstance(databaseTypeEnum, server, database, port, username, password, code);
94 CdmPersistentDataSource tmpDataSource = saveDataSource(TMP_DATASOURCE, dataSource);
95 boolean result = connectToDatasource(tmpDataSource);
96 CdmPersistentSourceUtils.delete(tmpDataSource);
97 return result;
98 }
99
100
101 /* (non-Javadoc)
102 * @see eu.etaxonomy.cdm.api.service.IDatabaseService#connectToDatabase(eu.etaxonomy.cdm.database.DatabaseTypeEnum, java.lang.String, java.lang.String, java.lang.String, java.lang.String)
103 */
104 public boolean connectToDatabase(DatabaseTypeEnum databaseTypeEnum, String server,
105 String database, String username, String password) throws TermNotFoundException {
106 return connectToDatabase(databaseTypeEnum, server, database, username, password, databaseTypeEnum.getDefaultPort(), null, null, null) ;
107 }
108
109 /* (non-Javadoc)
110 * @see eu.etaxonomy.cdm.api.service.IDatabaseService#saveDataSource(java.lang.String, eu.etaxonomy.cdm.database.ICdmDataSource)
111 */
112 public CdmPersistentDataSource saveDataSource(String strDataSourceName,
113 ICdmDataSource dataSource) {
114 return CdmPersistentDataSource.save(strDataSourceName, dataSource);
115 }
116
117 /* (non-Javadoc)
118 * @see eu.etaxonomy.cdm.api.service.IDatabaseService#updateDataSource(java.lang.String, eu.etaxonomy.cdm.database.CdmPersistentDataSource)
119 */
120 public CdmPersistentDataSource updateDataSource(String strDataSourceName,
121 CdmPersistentDataSource dataSource) throws DataSourceNotFoundException {
122 return CdmPersistentDataSource.update(strDataSourceName, dataSource);
123 }
124
125 /* (non-Javadoc)
126 * @see eu.etaxonomy.cdm.api.service.IDatabaseService#getUrl()
127 */
128 public String getUrl() {
129 return getDataSource().getUrl();
130 }
131
132 /* (non-Javadoc)
133 * @see eu.etaxonomy.cdm.api.service.IDatabaseService#getUsername()
134 */
135 public String getUsername() {
136 return getDataSource().getUsername();
137 }
138
139 /**
140 * Returns the AbstractDriverBasedDataSource from hibernate,
141 * generalized in order to also allow using SimpleDriverDataSource.
142 *
143 * @return the AbstractDriverBasedDataSource from the hibernate layer
144 */
145 private AbstractDriverBasedDataSource getDataSource(){
146 AbstractDriverBasedDataSource ds = (AbstractDriverBasedDataSource)SessionFactoryUtils.getDataSource(factory);
147 return ds;
148 }
149
150
151 /* (non-Javadoc)
152 * @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext)
153 */
154 public void setApplicationContext(ApplicationContext applicationContext)
155 throws BeansException {
156 this.appContext = applicationContext;
157 }
158
159
160
161 /* (non-Javadoc)
162 * @see eu.etaxonomy.cdm.api.service.IDatabaseService#getDbSchemaVersion()
163 */
164 @Override
165 public String getDbSchemaVersion() throws CdmSourceException {
166 try {
167 return (String)getSingleValue(MetaDataPropertyName.DB_SCHEMA_VERSION.getSqlQuery());
168 } catch (SQLException e) {
169 throw new CdmSourceException(e.getMessage());
170 }
171 }
172
173
174 /* (non-Javadoc)
175 * @see eu.etaxonomy.cdm.api.service.IDatabaseService#isDbEmpty()
176 */
177 @Override
178 public boolean isDbEmpty() throws CdmSourceException {
179 // Any CDM DB should have a schema version
180 String dbSchemaVersion = (String) getDbSchemaVersion();
181 return (dbSchemaVersion == null || dbSchemaVersion.equals(""));
182 }
183
184 /**
185 * Execute a SQL query which returns a single value
186 * FIXME:Remoting maybe not a smart idea to have this method here
187 * since private methods can also be accessed via reflection, which
188 * could be problematic specially for the case of remoting.
189 *
190 * @param query , which returns a single value
191 * @return
192 * @throws SQLException
193 */
194 private Object getSingleValue(String query) throws SQLException {
195 String queryString = query == null? "(null)": query;
196 ResultSet resultSet = executeQuery(query);
197 if (resultSet == null || resultSet.next() == false){
198 logger.info("No record returned for query " + queryString);
199 return null;
200 }
201 if (resultSet.getMetaData().getColumnCount() != 1){
202 logger.info("More than one column selected in query" + queryString);
203 //first value will be taken
204 }
205 Object object = resultSet.getObject(1);
206 if (resultSet.next()){
207 logger.info("Multiple results for query " + queryString);
208 //first row will be taken
209 }
210 return object;
211 }
212
213 /**
214 * Executes a query and returns the ResultSet.
215 * FIXME:Remoting maybe not a smart idea to have this method here
216 * since private methods can also be accessed via reflection, which
217 * could be problematic specially for the case of remoting.
218 *
219 * @return ResultSet for the query.
220 * @throws SQLException
221 */
222
223 private ResultSet executeQuery (String query) throws SQLException {
224
225 ResultSet resultSet;
226
227 if (query == null){
228 return null;
229 }
230
231 Connection connection = SessionFactoryUtils.getDataSource(factory).getConnection();
232 if (connection != null){
233 Statement statement = connection.createStatement();
234 resultSet = statement.executeQuery(query);
235 }else{
236 throw new RuntimeException("Could not establish connection to database");
237 }
238 return resultSet;
239
240 }
241
242
243 @Override
244 public Map<MetaDataPropertyName, String> getCdmMetadataMap() throws CdmSourceException {
245 Map<MetaDataPropertyName, String> cdmMetaDataMap = new HashMap<MetaDataPropertyName, String>();
246
247 for(MetaDataPropertyName mdpn : MetaDataPropertyName.values()){
248 String value = null;
249 try {
250 value = (String)getSingleValue(mdpn.getSqlQuery());
251 } catch (SQLException e) {
252 throw new CdmSourceException(e.getMessage());
253 }
254 if(value != null) {
255 cdmMetaDataMap.put(mdpn, value);
256 }
257 }
258 return cdmMetaDataMap;
259 }
260
261 }