Merge branch 'release/5.44.0'
[cdmlib.git] / cdmlib-persistence / src / main / java / eu / etaxonomy / cdm / database / CdmPersistentDataSource.java
1 /**
2 * Copyright (C) 2007 EDIT
3 * European Distributed Institute of Taxonomy
4 * http://www.e-taxonomy.eu
5 *
6 * The contents of this file are subject to the Mozilla Public License Version 1.1
7 * See LICENSE.TXT at the top of this package for the full license terms.
8 */
9 package eu.etaxonomy.cdm.database;
10
11 import static eu.etaxonomy.cdm.common.XmlHelp.getBeansRoot;
12 import static eu.etaxonomy.cdm.common.XmlHelp.insertXmlBean;
13 import static eu.etaxonomy.cdm.common.XmlHelp.insertXmlValueProperty;
14 import static eu.etaxonomy.cdm.common.XmlHelp.saveToXml;
15
16 import java.util.ArrayList;
17 import java.util.Enumeration;
18 import java.util.Iterator;
19 import java.util.List;
20 import java.util.Properties;
21
22 import javax.sql.DataSource;
23
24 import org.apache.logging.log4j.LogManager;
25 import org.apache.logging.log4j.Logger;
26 import org.hibernate.cache.spi.RegionFactory;
27 import org.jdom.Attribute;
28 import org.jdom.Element;
29 import org.springframework.beans.MutablePropertyValues;
30 import org.springframework.beans.factory.config.BeanDefinition;
31 import org.springframework.beans.factory.support.AbstractBeanDefinition;
32 import org.springframework.beans.factory.support.RootBeanDefinition;
33
34 import com.mchange.v2.c3p0.ComboPooledDataSource;
35
36 import eu.etaxonomy.cdm.common.XmlHelp;
37 import eu.etaxonomy.cdm.config.CdmPersistentSourceUtils;
38 import eu.etaxonomy.cdm.config.CdmPersistentXMLSource;
39 import eu.etaxonomy.cdm.config.CdmPersistentXMLSource.CdmSourceProperties;
40 import eu.etaxonomy.cdm.config.ICdmPersistentSource;
41 import eu.etaxonomy.cdm.database.types.IDatabaseType;
42 import eu.etaxonomy.cdm.persistence.hibernate.HibernateConfiguration;
43
44 /**
45 * Class to access a CdmDataSource which is stored in datasources.xml
46 */
47 public class CdmPersistentDataSource
48 extends CdmDataSourceBase
49 implements ICdmPersistentSource {
50
51 @SuppressWarnings("unused")
52 private static final Logger logger = LogManager.getLogger();
53
54 public static final String DATASOURCE_BEAN_POSTFIX = "DataSource";
55
56 private String beanName;
57
58 private String database;
59
60 /**
61 * This is strictly a <String, String> list of properties
62 */
63 private Properties cdmSourceProperties;
64
65 private List<Attribute> cdmSourceAttributes;
66
67 /**
68 * The Datasource class that Spring will use to set up the connection to the database
69 */
70 private static String dataSourceClassName = ComboPooledDataSource.class.getName();
71
72 /**
73 * Returns the default CdmDataSource
74 */
75 public final static CdmPersistentDataSource NewDefaultInstance() throws DataSourceNotFoundException {
76 return NewInstance("default");
77 }
78
79 /**
80 * Returns the CdmDataSource named by strDataSource
81 * @param strDataSource
82 * @return
83 */
84 public final static CdmPersistentDataSource NewInstance(String dataSourceName) throws DataSourceNotFoundException{
85 if (exists(dataSourceName)){
86 return new CdmPersistentDataSource(dataSourceName);
87 }else{
88 throw new DataSourceNotFoundException("Datasource not found: " + dataSourceName);
89 }
90 }
91
92 /**
93 * Private Constructor. Use NewXXX factory methods for creating a new instance of CdmDataSource!
94 * @param strDataSource
95 */
96 private CdmPersistentDataSource(String strDataSource){
97 setName(strDataSource);
98 loadSource(strDataSource);
99 }
100
101 private void loadSource(String strDataSource) {
102 CdmPersistentXMLSource cdmPersistentXMLSource = CdmPersistentXMLSource.NewInstance(strDataSource, DATASOURCE_BEAN_POSTFIX);
103 if(cdmPersistentXMLSource.getElement() != null) {
104 beanName = cdmPersistentXMLSource.getBeanName();
105 // properties from the persistent xml file
106 cdmSourceProperties = cdmPersistentXMLSource.getCdmSourceProperties();
107 cdmSourceAttributes = cdmPersistentXMLSource.getCdmSourceAttributes();
108
109 // added database specific properties if they are null
110 String url = getCdmSourceProperty(CdmSourceProperties.URL);
111 DatabaseTypeEnum dbTypeEnum = getDatabaseType();
112 if (dbTypeEnum != null && url != null){
113 IDatabaseType dbType = dbTypeEnum.getDatabaseType();
114 if (getCdmSourceProperty(CdmSourceProperties.DATABASE) == null){
115 String database = dbType.getDatabaseNameByConnectionString(url);
116 if(database != null) {
117 setDatabase(database);
118 }
119 }
120 if(getCdmSourceProperty(CdmSourceProperties.SERVER) == null){
121 String server = dbType.getServerNameByConnectionString(url);
122 if(server != null) {
123 setServer(server);
124 }
125 }
126 if(getCdmSourceProperty(CdmSourceProperties.PORT) == null){
127 int port = dbType.getPortByConnectionString(url);
128 if(port > 0) {
129 setPort(port);
130 } else {
131 setPort(NULL_PORT);
132 }
133 }
134 }
135 }
136 }
137
138 @Override
139 public String getBeanName() {
140 return beanName;
141 }
142
143 @Override
144 public String getDatabase() {
145 return database;
146 }
147
148 @Override
149 public void setDatabase(String database) {
150 this.database = database;
151 //update url string
152 cdmSourceProperties.put(CdmSourceProperties.URL.toString(), getDatabaseType().getConnectionString(this));
153 }
154
155 @Override
156 public void setServer(String server) {
157 super.setServer(server);
158 //update url string
159 cdmSourceProperties.put(CdmSourceProperties.URL.toString(), getDatabaseType().getConnectionString(this));
160 }
161
162 @Override
163 public void setPort(int port) {
164 super.setPort(port);
165 if(port != NULL_PORT) {
166 //update url string
167 cdmSourceProperties.put(CdmSourceProperties.URL.toString(), getDatabaseType().getConnectionString(this));
168 }
169 }
170
171 @Override
172 public String getFilePath() {
173 return getCdmSourceProperty(CdmSourceProperties.FILEPATH);
174 }
175
176 @Override
177 public H2Mode getMode() {
178 return H2Mode.fromString(getCdmSourceProperty(CdmSourceProperties.MODE));
179 }
180
181 @Override
182 public void setMode(H2Mode h2Mode) {
183 cdmSourceProperties.put(CdmSourceProperties.MODE.toString(), h2Mode.name());
184 }
185
186 @Override
187 public String getUsername(){
188 return getCdmSourceProperty(CdmSourceProperties.USERNAME);
189 }
190
191 @Override
192 public void setUsername(String username) {
193 cdmSourceProperties.put(CdmSourceProperties.USERNAME.toString(), username);
194 }
195
196 @Override
197 public String getPassword(){
198 return getCdmSourceProperty(CdmSourceProperties.PASSWORD);
199 }
200
201 @Override
202 public void setPassword(String password) {
203 cdmSourceProperties.put(CdmSourceProperties.PASSWORD.toString(), password);
204 }
205
206 @Override
207 public DatabaseTypeEnum getDatabaseType(){
208 String strDriverClass = getCdmSourceProperty(CdmSourceProperties.DRIVER_CLASS);
209 DatabaseTypeEnum dbType = DatabaseTypeEnum.byDriverClass(strDriverClass);
210 return dbType;
211 }
212
213 public String getCdmSourceProperty(CdmSourceProperties property){
214 return cdmSourceProperties.getProperty(property.toString(),null);
215 }
216
217 /**
218 * Returns a BeanDefinition object of type DataSource that contains
219 * datsource properties (url, username, password, ...)
220 * @return
221 */
222 @SuppressWarnings("unchecked")
223 @Override
224 public BeanDefinition getDatasourceBean(){
225 DatabaseTypeEnum dbtype =
226 DatabaseTypeEnum.byDriverClass(getCdmSourceProperty(CdmSourceProperties.DRIVER_CLASS));
227
228 AbstractBeanDefinition bd = new RootBeanDefinition(dbtype.getDataSourceClass());
229 //attributes
230 Iterator<Attribute> iterator = cdmSourceAttributes.iterator();
231 while(iterator.hasNext()){
232 Attribute attribute = iterator.next();
233 if (attribute.getName().equals("lazy-init")){
234 bd.setLazyInit(Boolean.valueOf(attribute.getValue()));
235 }
236 if (attribute.getName().equals("init-method")){
237 bd.setInitMethodName(attribute.getValue());
238 }
239 if (attribute.getName().equals("destroy-method")){
240 bd.setDestroyMethodName(attribute.getValue());
241 }
242 //Attribute attribute = iterator.next();
243 //bd.setAttribute(attribute.getName(), attribute.getValue());
244 }
245
246 //properties
247 MutablePropertyValues props = new MutablePropertyValues();
248
249 Enumeration<String> keys = (Enumeration)cdmSourceProperties.keys();
250 while (keys.hasMoreElements()){
251 String key = keys.nextElement();
252
253 props.addPropertyValue(key, cdmSourceProperties.getProperty(key));
254 }
255
256 bd.setPropertyValues(props);
257 return bd;
258 }
259
260 @Override
261 public BeanDefinition getHibernatePropertiesBean(DbSchemaValidation hbm2dll){
262 HibernateConfiguration hibernateConfig = HibernateConfiguration.NewDefaultInstance();
263 return getHibernatePropertiesBean(hbm2dll, hibernateConfig);
264 }
265
266 @Override
267 @Deprecated
268 public BeanDefinition getHibernatePropertiesBean(DbSchemaValidation hbm2dll, Boolean showSql, Boolean formatSql,
269 Boolean registerSearchListener, Class<? extends RegionFactory> cacheProviderClass, String byteCodeProvider){
270 HibernateConfiguration hibernateConfig = HibernateConfiguration.NewInstance(showSql, formatSql,
271 registerSearchListener, null, cacheProviderClass, byteCodeProvider);
272 return this.getHibernatePropertiesBean(hbm2dll, hibernateConfig);
273 }
274
275 @Override
276 public BeanDefinition getHibernatePropertiesBean(DbSchemaValidation hbm2dll,
277 HibernateConfiguration hibernateConfig) {
278
279 if (hibernateConfig == null){
280 hibernateConfig = HibernateConfiguration.NewDefaultInstance();
281 }
282 boolean showSql = hibernateConfig.getShowSql();
283 boolean formatSql = hibernateConfig.getFormatSql();
284 boolean registerAuditing = hibernateConfig.getRegisterEnvers();
285 boolean registerSearchListener = hibernateConfig.getRegisterSearch();
286 Class<? extends RegionFactory> cacheProviderClass = hibernateConfig.getCacheProviderClass();
287 String byteCodeProvider = hibernateConfig.getByteCodeProvider();
288
289 //Hibernate default values
290 if (hbm2dll == null){
291 hbm2dll = DbSchemaValidation.VALIDATE;
292 }
293
294 return makeHibernatePropertiesBean(getDatabaseType(), hbm2dll, showSql, formatSql, registerAuditing,
295 registerSearchListener, cacheProviderClass, byteCodeProvider);
296 }
297
298 /**
299 * Tests existing of the datsource in the according config file.
300 * @return true if a datasource with the given name exists in the according datasource config file.
301 */
302 public static boolean exists(String strDataSourceName){
303 Element bean = CdmPersistentSourceUtils.getCdmSourceBeanXml(strDataSourceName, DATASOURCE_BEAN_POSTFIX);
304 return (bean != null);
305 }
306
307 /**
308 * @param strDataSourceName
309 * @param dataSource
310 * @param code
311 * @return
312 * the updated dataSource, null if not succesful
313 */
314 public static CdmPersistentDataSource update(String strDataSourceName,
315 ICdmDataSource dataSource) throws DataSourceNotFoundException, IllegalArgumentException{
316 CdmPersistentSourceUtils.delete(CdmPersistentSourceUtils.getBeanName(strDataSourceName,DATASOURCE_BEAN_POSTFIX));
317 return save(strDataSourceName, dataSource);
318 }
319
320 /**
321 * Replace the persisted datasource with another one.
322 * Used primarily for renaming a datasource.
323 *
324 * @param strDataSourceName
325 * @param dataSource
326 * @return
327 * @throws DataSourceNotFoundException
328 * @throws IllegalArgumentException
329 */
330 public static CdmPersistentDataSource replace(String strDataSourceName,
331 ICdmDataSource dataSource) throws DataSourceNotFoundException, IllegalArgumentException{
332 CdmPersistentSourceUtils.delete(CdmPersistentSourceUtils.getBeanName(strDataSourceName,DATASOURCE_BEAN_POSTFIX));
333 return save(dataSource);
334 }
335
336 /**
337 * @param dataSource
338 * @return
339 * @throws IllegalArgumentException
340 */
341 public static CdmPersistentDataSource save(ICdmDataSource dataSource) throws IllegalArgumentException {
342 return save(dataSource.getName(),dataSource);
343 }
344
345 /**
346 *
347 * @param strDataSourceName
348 * @param databaseTypeEnum
349 * @param server
350 * @param database
351 * @param port
352 * @param username
353 * @param password
354 * @param dataSourceClass
355 * @param initMethod
356 * @param destroyMethod
357 * @param startSilent
358 * @param startServer
359 * @param filePath
360 * @param mode
361 * @return
362 */
363 private static CdmPersistentDataSource save(String strDataSourceName,
364 DatabaseTypeEnum databaseTypeEnum,
365 String server,
366 String database,
367 String port,
368 String username,
369 String password,
370 Class<? extends DataSource> dataSourceClass,
371 String initMethod,
372 String destroyMethod,
373 Boolean startSilent,
374 Boolean startServer,
375 String filePath,
376 H2Mode mode
377 ){
378
379 int portNumber = "".equals(port) ? databaseTypeEnum.getDefaultPort() : Integer.valueOf(port);
380
381 ICdmDataSource dataSource = new CdmDataSource(databaseTypeEnum, server, database, portNumber, username, password, filePath, mode);
382
383 //root
384 Element root = getBeansRoot(CdmPersistentSourceUtils.getCdmSourceInputStream());
385 if (root == null){
386 return null;
387 }
388 //bean
389 Element bean = XmlHelp.getFirstAttributedChild(root, "bean", "id", CdmPersistentSourceUtils.getBeanName(strDataSourceName, DATASOURCE_BEAN_POSTFIX));
390 if (bean != null){
391 bean.detach(); //delete old version if necessary
392 }
393 bean = insertXmlBean(root, CdmPersistentSourceUtils.getBeanName(strDataSourceName, DATASOURCE_BEAN_POSTFIX), dataSourceClass.getName());
394 //attributes
395 bean.setAttribute("lazy-init", "true");
396 if (initMethod != null) {bean.setAttribute("init-method", initMethod);}
397 if (destroyMethod != null) {bean.setAttribute("destroy-method", destroyMethod);}
398
399 //set properties
400 insertXmlValueProperty(bean, "driverClassName", databaseTypeEnum.getDriverClassName());
401
402 insertXmlValueProperty(bean, "url", databaseTypeEnum.getConnectionString(dataSource));
403 if (username != null) {insertXmlValueProperty(bean, "username", username );}
404 if (password != null) {insertXmlValueProperty(bean, "password", password );}
405 if (startSilent != null) {insertXmlValueProperty(bean, "startSilent", startSilent.toString() );}
406 if (startServer != null) {insertXmlValueProperty(bean, "startServer", startServer.toString() );}
407 if (filePath != null) {insertXmlValueProperty(bean, "filePath", filePath );}
408 if (mode != null) {insertXmlValueProperty(bean, "mode", mode.toString() );}
409
410 //save
411 saveToXml(root.getDocument(),
412 CdmPersistentSourceUtils.getResourceDirectory(),
413 CdmPersistentXMLSource.CDMSOURCE_FILE_NAME,
414 XmlHelp.prettyFormat );
415 try {
416 return NewInstance(strDataSourceName) ;
417 } catch (DataSourceNotFoundException e) {
418 throw new RuntimeException("Error when saving CdmPersistentDatasource", e);
419 }
420 }
421
422 /**
423 * Saves a datasource to the datasource config file. If strDataSourceName differs a new dataSource
424 * will be created in config file. Use update() of real update functionality.
425 *
426 * @param strDataSourceName
427 * @param dataSource
428 * @return
429 */
430 public static CdmPersistentDataSource save(String strDataSourceName,
431 ICdmDataSource dataSource) throws IllegalArgumentException{
432
433 if(dataSource.getDatabaseType() == null){
434 throw new IllegalArgumentException("Database type not specified");
435 }
436
437 if(dataSource.getDatabaseType().equals(DatabaseTypeEnum.H2)){
438 Class<? extends DataSource> dataSourceClass = LocalH2.class;
439 if(dataSource.getMode() == null) {
440 throw new IllegalArgumentException("H2 mode not specified");
441 }
442 return save(
443 strDataSourceName,
444 dataSource.getDatabaseType(),
445 "localhost",
446 getCheckedDataSourceParameter(dataSource.getDatabase()),
447 dataSource.getDatabaseType().getDefaultPort() + "",
448 getCheckedDataSourceParameter(dataSource.getUsername()),
449 getCheckedDataSourceParameter(dataSource.getPassword()),
450 dataSourceClass,
451 null, null, null, null,
452 dataSource.getFilePath(),
453 dataSource.getMode()
454 );
455 }else{
456
457 Class<? extends DataSource> dataSourceClass;
458 try {
459 dataSourceClass = (Class<? extends DataSource>) Class.forName(dataSourceClassName);
460 String server = getCheckedDataSourceParameter(dataSource.getServer());
461 CdmPersistentDataSource persistendDatasource = save(
462 strDataSourceName,
463 dataSource.getDatabaseType(),
464 getCheckedDataSourceParameter(dataSource.getServer()),
465 getCheckedDataSourceParameter(dataSource.getDatabase()),
466 dataSource.getPort() + "",
467 getCheckedDataSourceParameter(dataSource.getUsername()),
468 getCheckedDataSourceParameter(dataSource.getPassword()),
469 dataSourceClass,
470 null, null, null, null, null, null
471 );
472
473 return persistendDatasource;
474 } catch (ClassNotFoundException e) {
475 throw new RuntimeException("DataSourceClass not found: " + dataSourceClassName, e);
476 }
477 }
478 }
479
480 private static String getCheckedDataSourceParameter(String parameter) throws IllegalArgumentException{
481 if(parameter != null) {
482 return parameter;
483 } else {
484 throw new IllegalArgumentException("Non obsolete paramater was assigned a null value: " + parameter);
485 }
486 }
487
488 /**
489 * Returns a list of all datasources stored in the datasource config file
490 * @return all existing data sources
491 */
492 @SuppressWarnings("unchecked")
493 static public List<CdmPersistentDataSource> getAllDataSources(){
494 List<CdmPersistentDataSource> dataSources = new ArrayList<>();
495
496 Element root = getBeansRoot(CdmPersistentSourceUtils.getCdmSourceInputStream());
497 if (root == null){
498 return null;
499 }else{
500 List<Element> lsChildren = root.getChildren("bean", root.getNamespace());
501
502 for (Element elBean : lsChildren){
503 String strId = elBean.getAttributeValue("id");
504 if (strId != null && strId.endsWith(DATASOURCE_BEAN_POSTFIX)){
505 strId = strId.replace(DATASOURCE_BEAN_POSTFIX, "");
506 dataSources.add(new CdmPersistentDataSource(strId));
507 }
508 }
509 }
510 return dataSources;
511 }
512
513 @Override
514 public boolean equals(Object obj){
515 if (obj == null){
516 return false;
517 }else if (! CdmPersistentDataSource.class.isAssignableFrom(obj.getClass())){
518 return false;
519 }else{
520 CdmPersistentDataSource dataSource = (CdmPersistentDataSource)obj;
521 return (getName() == dataSource.getName());
522 }
523 }
524
525 @Override
526 public String toString(){
527 if (getName() != null){
528 return getName();
529 }else{
530 return super.toString();
531 }
532 }
533 }