Project

General

Profile

Download (19.5 KB) Statistics
| Branch: | Tag: | Revision:
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

    
10
package eu.etaxonomy.cdm.database;
11

    
12
import static eu.etaxonomy.cdm.common.XmlHelp.getBeansRoot;
13
import static eu.etaxonomy.cdm.common.XmlHelp.insertXmlBean;
14
import static eu.etaxonomy.cdm.common.XmlHelp.insertXmlValueProperty;
15
import static eu.etaxonomy.cdm.common.XmlHelp.saveToXml;
16

    
17
import java.util.ArrayList;
18
import java.util.Enumeration;
19
import java.util.Iterator;
20
import java.util.List;
21
import java.util.Properties;
22

    
23
import javax.sql.DataSource;
24

    
25
import org.apache.log4j.Logger;
26
import org.hibernate.cache.internal.NoCachingRegionFactory;
27
import org.hibernate.cache.spi.RegionFactory;
28
import org.jdom.Attribute;
29
import org.jdom.Element;
30
import org.springframework.beans.MutablePropertyValues;
31
import org.springframework.beans.factory.config.BeanDefinition;
32
import org.springframework.beans.factory.config.PropertiesFactoryBean;
33
import org.springframework.beans.factory.support.AbstractBeanDefinition;
34
import org.springframework.beans.factory.support.RootBeanDefinition;
35

    
36
import com.mchange.v2.c3p0.ComboPooledDataSource;
37

    
38
import eu.etaxonomy.cdm.common.XmlHelp;
39
import eu.etaxonomy.cdm.config.CdmPersistentSourceUtils;
40
import eu.etaxonomy.cdm.config.CdmPersistentXMLSource;
41
import eu.etaxonomy.cdm.config.CdmPersistentXMLSource.CdmSourceProperties;
42
import eu.etaxonomy.cdm.config.ICdmPersistentSource;
43
import eu.etaxonomy.cdm.database.types.IDatabaseType;
44
import eu.etaxonomy.cdm.model.name.NomenclaturalCode;
45

    
46

    
47
/**
48
 * class to access an CdmDataSource
49
 */
50
public class CdmPersistentDataSource extends CdmDataSourceBase implements ICdmPersistentSource {
51
	private static final Logger logger = Logger.getLogger(CdmPersistentDataSource.class);
52
	
53
	public static final String DATASOURCE_BEAN_POSTFIX = "DataSource";
54

    
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
	/**
69
	 * The Datasource class that Spring will use to set up the connection to the database
70
	 */
71
	private static String dataSourceClassName = ComboPooledDataSource.class.getName();	
72

    
73
	
74
	/**
75
	 * Returns the default CdmDataSource
76
	 * @return the default CdmDataSource
77
	 * @throws DataSourceNotFoundException 
78
	 */
79
	public final static CdmPersistentDataSource NewDefaultInstance() throws DataSourceNotFoundException {
80
		return NewInstance("default");
81
	}
82
	
83
	
84
	/**
85
	 * Returns the default CdmDataSource
86
	 * @return the default CdmDataSource
87
	 * @throws DataSourceNotFoundException 
88
	 */
89
	public final static CdmPersistentDataSource NewLocalHsqlInstance() throws DataSourceNotFoundException{
90
		return NewInstance("localDefaultHsql");
91
	}
92
	
93
	/**
94
	 * Returns the CdmDataSource named by strDataSource
95
	 * @param strDataSource
96
	 * @return
97
	 */
98
	public final static CdmPersistentDataSource NewInstance(String dataSourceName) throws DataSourceNotFoundException{
99
		if (exists(dataSourceName)){
100
			return new CdmPersistentDataSource(dataSourceName);
101
		}else{
102
			throw new DataSourceNotFoundException("Datasource not found: " + dataSourceName);
103
		}
104
	}
105

    
106
	/**
107
	 * Private Constructor. Use NewXXX factory methods for creating a new instance of CdmDataSource!
108
	 * @param strDataSource
109
	 */
110
	private CdmPersistentDataSource(String strDataSource){
111
		setName(strDataSource);
112
		loadSource(strDataSource);		
113
	}
114
	
115
	private void loadSource(String strDataSource) {
116
		CdmPersistentXMLSource cdmPersistentXMLSource = CdmPersistentXMLSource.NewInstance(strDataSource, DATASOURCE_BEAN_POSTFIX);
117
		if(cdmPersistentXMLSource.getElement() != null) {
118
			beanName = cdmPersistentXMLSource.getBeanName();
119
			// properties from the persistent xml file 
120
			cdmSourceProperties = cdmPersistentXMLSource.getCdmSourceProperties();
121
			cdmSourceAttributes = cdmPersistentXMLSource.getCdmSourceAttributes();
122
			
123
			// added database specific properties if they are null
124
			String url = getCdmSourceProperty(CdmSourceProperties.URL);
125
			DatabaseTypeEnum dbTypeEnum = getDatabaseType();
126
			if (dbTypeEnum != null && url != null){
127
				IDatabaseType dbType = dbTypeEnum.getDatabaseType();				
128
				if (getCdmSourceProperty(CdmSourceProperties.DATABASE) == null){
129
					String database = dbType.getDatabaseNameByConnectionString(url);
130
					if(database != null) {
131
						setDatabase(database);
132
					}
133
				}
134
				if(getCdmSourceProperty(CdmSourceProperties.SERVER) == null){
135
					String server = dbType.getServerNameByConnectionString(url);
136
					if(server != null) {
137
						setServer(server);
138
					}
139
				}
140
				if(getCdmSourceProperty(CdmSourceProperties.PORT) == null){
141
					int port = dbType.getPortByConnectionString(url); 		
142
						if(port > 0) {
143
							setPort(port);
144
						} else {
145
							setPort(NULL_PORT);
146
						}
147
				}
148
			}
149
		}					
150
	}
151
	
152
	public String getBeanName() {
153
		return beanName;
154
	}
155
	
156
	@Override
157
	public String getDatabase() {
158
		return database;
159
	}
160

    
161

    
162
	@Override
163
	public void setDatabase(String database) {
164
		this.database = database;
165
		//update url string
166
		cdmSourceProperties.put(CdmSourceProperties.URL.toString(), getDatabaseType().getConnectionString(this));
167
		
168
	}
169
	
170
	@Override
171
	public void setServer(String server) {
172
		super.setServer(server);
173
		//update url string
174
		cdmSourceProperties.put(CdmSourceProperties.URL.toString(), getDatabaseType().getConnectionString(this));
175
	}
176
		
177
	@Override
178
	public void setPort(int port) {
179
		super.setPort(port);
180
		if(port != NULL_PORT) {			
181
			//update url string
182
			cdmSourceProperties.put(CdmSourceProperties.URL.toString(), getDatabaseType().getConnectionString(this));
183
		}
184
	}
185
	@Override
186
	public String getFilePath() {		
187
		return getCdmSourceProperty(CdmSourceProperties.FILEPATH);
188
	}
189

    
190

    
191
	@Override
192
	public H2Mode getMode() {		
193
		return H2Mode.fromString(getCdmSourceProperty(CdmSourceProperties.MODE));
194
	}
195
	
196
	@Override
197
	public void setMode(H2Mode h2Mode) {
198
		cdmSourceProperties.put(CdmSourceProperties.MODE.toString(), h2Mode.name());
199
		
200
	}
201
	
202
	@Override
203
	public String getUsername(){
204
		return getCdmSourceProperty(CdmSourceProperties.USERNAME);
205
	}
206
	
207
	@Override
208
	public void setUsername(String username) {
209
		cdmSourceProperties.put(CdmSourceProperties.USERNAME.toString(), username);
210
		
211
	}
212
	
213
	@Override
214
	public String getPassword(){
215
		return getCdmSourceProperty(CdmSourceProperties.PASSWORD);
216
	}
217
	
218
	@Override
219
	public void setPassword(String password) {
220
		cdmSourceProperties.put(CdmSourceProperties.PASSWORD.toString(), password);
221
		
222
	}
223

    
224
	@Override
225
	public NomenclaturalCode getNomenclaturalCode() {
226
		// TODO null
227
		return NomenclaturalCode.fromString(getCdmSourceProperty(CdmSourceProperties.NOMENCLATURAL_CODE));
228
	}
229

    
230
	@Override
231
	public void setNomenclaturalCode(NomenclaturalCode nomenclaturalCode) {
232
		cdmSourceProperties.put(CdmSourceProperties.NOMENCLATURAL_CODE.toString(), nomenclaturalCode.name());
233
	}
234

    
235

    
236

    
237
	@Override
238
	public DatabaseTypeEnum getDatabaseType(){
239
		String strDriverClass = getCdmSourceProperty(CdmSourceProperties.DRIVER_CLASS);
240
		DatabaseTypeEnum dbType = DatabaseTypeEnum.byDriverClass(strDriverClass);
241
		return dbType;
242
	}
243
		
244
	
245
	public String getCdmSourceProperty(CdmSourceProperties property){		
246
		return cdmSourceProperties.getProperty(property.toString(),null);
247
	}
248

    
249
	/**
250
	 * Returns a BeanDefinition object of type DataSource that contains
251
	 * datsource properties (url, username, password, ...)
252
	 * @return
253
	 */
254
	@SuppressWarnings("unchecked")
255
	@Override
256
	public BeanDefinition getDatasourceBean(){
257
		DatabaseTypeEnum dbtype = 
258
				DatabaseTypeEnum.byDriverClass(getCdmSourceProperty(CdmSourceProperties.DRIVER_CLASS));
259
		
260
		AbstractBeanDefinition bd = new RootBeanDefinition(dbtype.getDataSourceClass());
261
		//attributes
262
		Iterator<Attribute> iterator = cdmSourceAttributes.iterator();
263
		while(iterator.hasNext()){
264
			Attribute attribute = iterator.next();
265
			if (attribute.getName().equals("lazy-init")){
266
				bd.setLazyInit(Boolean.valueOf(attribute.getValue()));
267
			}
268
			if (attribute.getName().equals("init-method")){
269
				bd.setInitMethodName(attribute.getValue());
270
			}
271
			if (attribute.getName().equals("destroy-method")){
272
				bd.setDestroyMethodName(attribute.getValue());
273
			}
274
			//Attribute attribute = iterator.next();
275
			//bd.setAttribute(attribute.getName(), attribute.getValue());
276
		}		
277
		
278
		//properties
279
		MutablePropertyValues props = new MutablePropertyValues();
280
		
281
		Enumeration<String> keys = (Enumeration)cdmSourceProperties.keys(); 
282
		while (keys.hasMoreElements()){
283
			String key = (String)keys.nextElement();
284
			
285
			if (key.equals("nomenclaturalCode") && cdmSourceProperties.getProperty(key).equals("ICBN")){
286
				//bugfix for old nomenclatural codes, remove if fixed elsewhere, see https://dev.e-taxonomy.eu/trac/ticket/3658
287
				props.addPropertyValue(key, NomenclaturalCode.ICNAFP.name());
288
			}else{
289
				props.addPropertyValue(key, cdmSourceProperties.getProperty(key));
290
			}
291
		}
292

    
293
		
294
		
295
		bd.setPropertyValues(props);
296
		return bd;
297
	}
298
	
299
	/**
300
	 * @param hbm2dll
301
	 * @param showSql
302
	 * @return
303
	 */
304
	@Override
305
	public BeanDefinition getHibernatePropertiesBean(DbSchemaValidation hbm2dll){
306
		boolean showSql = false;
307
		boolean formatSql = false;
308
		boolean registerSearchListener = false;
309
		Class<? extends RegionFactory> cacheProviderClass = NoCachingRegionFactory.class;
310
		return getHibernatePropertiesBean(hbm2dll, showSql, formatSql, registerSearchListener, cacheProviderClass);
311
	}
312
	
313
	
314
	/**
315
	 * @param hbm2dll
316
	 * @param showSql
317
	 * @return
318
	 */
319
	@Override
320
	public BeanDefinition getHibernatePropertiesBean(DbSchemaValidation hbm2dll, Boolean showSql, Boolean formatSql, Boolean registerSearchListener, Class<? extends RegionFactory> cacheProviderClass){
321
		//Hibernate default values
322
		if (hbm2dll == null){
323
			hbm2dll = DbSchemaValidation.VALIDATE;
324
		}
325
		if (showSql == null){
326
			showSql = false;
327
		}
328
		if (formatSql == null){
329
			formatSql = false;
330
		}
331
		if (cacheProviderClass == null){
332
			cacheProviderClass = NoCachingRegionFactory.class;
333
		}
334
		if(registerSearchListener == null){
335
			registerSearchListener = false;
336
		}
337
				
338
		DatabaseTypeEnum dbtype = getDatabaseType();
339
		AbstractBeanDefinition bd = new RootBeanDefinition(PropertiesFactoryBean.class);
340
		MutablePropertyValues hibernateProps = new MutablePropertyValues();
341

    
342
		Properties props = new Properties();
343
		props.setProperty("hibernate.hbm2ddl.auto", hbm2dll.toString());
344
		props.setProperty("hibernate.dialect", dbtype.getHibernateDialectCanonicalName());
345
		props.setProperty("hibernate.cache.region.factory_class", cacheProviderClass.getName());
346
		props.setProperty("hibernate.show_sql", String.valueOf(showSql));
347
		props.setProperty("hibernate.format_sql", String.valueOf(formatSql));
348
		props.setProperty("hibernate.search.autoregister_listeners", String.valueOf(registerSearchListener));
349

    
350
		hibernateProps.addPropertyValue("properties",props);
351
		bd.setPropertyValues(hibernateProps);
352
		return bd;
353
	}
354
	
355
	
356
	/**
357
	 * Tests existing of the datsource in the according config  file.
358
	 * @return true if a datasource with the given name exists in the according datasource config file.
359
	 */
360
	public static boolean exists(String strDataSourceName){
361
		Element bean = CdmPersistentSourceUtils.getCdmSourceBeanXml(strDataSourceName, DATASOURCE_BEAN_POSTFIX);
362
		return (bean != null);
363
	}
364

    
365
	/**
366
	 * @param strDataSourceName
367
	 * @param dataSource
368
	 * @param code 
369
	 * @return
370
	 * 			the updated dataSource, null if not succesful
371
	 */
372
	public static CdmPersistentDataSource update(String strDataSourceName,
373
			ICdmDataSource dataSource) throws DataSourceNotFoundException, IllegalArgumentException{
374
		CdmPersistentSourceUtils.delete(CdmPersistentSourceUtils.getBeanName(strDataSourceName,DATASOURCE_BEAN_POSTFIX));
375
		return save(strDataSourceName, dataSource);
376
	}
377
	
378
	/**
379
	 * Replace the persisted datasource with another one.
380
	 * Used primarily for renaming a datasource.
381
	 * 
382
	 * @param strDataSourceName
383
	 * @param dataSource
384
	 * @return
385
	 * @throws DataSourceNotFoundException
386
	 * @throws IllegalArgumentException
387
	 */
388
	public static CdmPersistentDataSource replace(String strDataSourceName,
389
			ICdmDataSource dataSource) throws DataSourceNotFoundException, IllegalArgumentException{
390
		CdmPersistentSourceUtils.delete(CdmPersistentSourceUtils.getBeanName(strDataSourceName,DATASOURCE_BEAN_POSTFIX));
391
		return save(dataSource);
392
	}
393
	
394
	/**
395
	 * @param dataSource
396
	 * @return
397
	 * @throws IllegalArgumentException
398
	 */
399
	public static CdmPersistentDataSource save(ICdmDataSource dataSource)  throws IllegalArgumentException {
400
		return save(dataSource.getName(),dataSource);
401
	}
402
	
403
	/**
404
	 * 
405
	 * @param strDataSourceName
406
	 * @param databaseTypeEnum
407
	 * @param server
408
	 * @param database
409
	 * @param port
410
	 * @param username
411
	 * @param password
412
	 * @param dataSourceClass
413
	 * @param initMethod
414
	 * @param destroyMethod
415
	 * @param startSilent
416
	 * @param startServer
417
	 * @param filePath
418
	 * @param mode
419
	 * @return
420
	 */
421
	private static CdmPersistentDataSource save(String strDataSourceName, 
422
			DatabaseTypeEnum databaseTypeEnum, 
423
			String server, 
424
			String database, 
425
			String port, 
426
			String username, 
427
			String password, 
428
			Class<? extends DataSource> dataSourceClass,
429
			String initMethod,
430
			String destroyMethod,
431
			Boolean startSilent,
432
			Boolean startServer, 
433
			String filePath,
434
			H2Mode mode,
435
			NomenclaturalCode code
436
		){
437
		
438
		int portNumber = "".equals(port) ? databaseTypeEnum.getDefaultPort() : Integer.valueOf(port);
439
		
440
		ICdmDataSource dataSource = new CdmDataSource(databaseTypeEnum, server, database, portNumber, username, password, filePath, mode, code);
441
				
442
		//root
443
		Element root = getBeansRoot(CdmPersistentSourceUtils.getCdmSourceInputStream());
444
		if (root == null){
445
			return null;
446
		}
447
		//bean
448
		Element bean = XmlHelp.getFirstAttributedChild(root, "bean", "id", CdmPersistentSourceUtils.getBeanName(strDataSourceName, DATASOURCE_BEAN_POSTFIX));
449
		if (bean != null){
450
			bean.detach();  //delete old version if necessary
451
		}
452
		bean = insertXmlBean(root, CdmPersistentSourceUtils.getBeanName(strDataSourceName, DATASOURCE_BEAN_POSTFIX), dataSourceClass.getName());
453
		//attributes
454
		bean.setAttribute("lazy-init", "true");
455
		if (initMethod != null) {bean.setAttribute("init-method", initMethod);}
456
		if (destroyMethod != null) {bean.setAttribute("destroy-method", destroyMethod);}
457
		
458
		//set properties
459
		insertXmlValueProperty(bean, "driverClassName", databaseTypeEnum.getDriverClassName());
460
		
461
		insertXmlValueProperty(bean, "url", databaseTypeEnum.getConnectionString(dataSource));
462
		if (username != null) {insertXmlValueProperty(bean, "username", username );}
463
		if (password != null) {insertXmlValueProperty(bean, "password", password );}
464
		if (startSilent != null) {insertXmlValueProperty(bean, "startSilent", startSilent.toString() );}
465
		if (startServer != null) {insertXmlValueProperty(bean, "startServer", startServer.toString() );}
466
		if (filePath != null) {insertXmlValueProperty(bean, "filePath", filePath );}
467
		if (mode != null) {insertXmlValueProperty(bean, "mode", mode.toString() );}
468
		if (code != null) {insertXmlValueProperty(bean, "nomenclaturalCode", code.name());}
469
		
470
		//save
471
		saveToXml(root.getDocument(), 
472
				CdmPersistentSourceUtils.getResourceDirectory(), 
473
				CdmPersistentXMLSource.CDMSOURCE_FILE_NAME, 
474
				XmlHelp.prettyFormat );
475
		try {
476
			return NewInstance(strDataSourceName) ;
477
		} catch (DataSourceNotFoundException e) {
478
			logger.error("Error when saving datasource");
479
			return null;
480
		}
481
	}
482
	
483

    
484
	/**
485
	 * Saves a datasource to the datasource config file. If strDataSourceName differs a new dataSource
486
	 * will be created in config file. Use update() of real update functionality.
487
	 * 
488
	 * @param strDataSourceName
489
	 * @param dataSource
490
	 * @return
491
	 */
492
	public static CdmPersistentDataSource save(String strDataSourceName,
493
			ICdmDataSource dataSource)  throws IllegalArgumentException{
494
		
495
		if(dataSource.getDatabaseType() == null){
496
			new IllegalArgumentException("Database type not specified");
497
		}
498
		
499
		if(dataSource.getDatabaseType().equals(DatabaseTypeEnum.H2)){
500
			Class<? extends DataSource> dataSourceClass =  LocalH2.class;
501
			if(dataSource.getMode() == null) {
502
				new IllegalArgumentException("H2 mode not specified");
503
			}			
504
			return save(
505
					strDataSourceName, 
506
					dataSource.getDatabaseType(), 
507
					"localhost", 
508
					getCheckedDataSourceParameter(dataSource.getDatabase()), 
509
					dataSource.getDatabaseType().getDefaultPort() + "", 
510
					getCheckedDataSourceParameter(dataSource.getUsername()), 
511
					getCheckedDataSourceParameter(dataSource.getPassword()), 
512
					dataSourceClass, 
513
					null, null, null, null, 
514
					dataSource.getFilePath(), 
515
					dataSource.getMode(),
516
					dataSource.getNomenclaturalCode());
517
		}else{
518
			
519
			Class<? extends DataSource> dataSourceClass;
520
			try {
521
				dataSourceClass = (Class<? extends DataSource>) Class.forName(dataSourceClassName);
522
				String server = getCheckedDataSourceParameter(dataSource.getServer());
523
				CdmPersistentDataSource persistendDatasource =  save(
524
					strDataSourceName, 
525
					dataSource.getDatabaseType(), 
526
					getCheckedDataSourceParameter(dataSource.getServer()), 
527
					getCheckedDataSourceParameter(dataSource.getDatabase()), 
528
					dataSource.getPort() + "", 
529
					getCheckedDataSourceParameter(dataSource.getUsername()), 
530
					getCheckedDataSourceParameter(dataSource.getPassword()), 
531
					dataSourceClass, 
532
					null, null, null, null, null, null,
533
					dataSource.getNomenclaturalCode());
534
				
535
				return persistendDatasource;
536
			} catch (ClassNotFoundException e) {
537
				logger.error("DataSourceClass not found - stopping application", e);
538
				System.exit(-1);
539
			}
540
			// will never be reached
541
			return null;
542
		}
543
	}
544

    
545
	private static String getCheckedDataSourceParameter(String parameter) throws IllegalArgumentException{		
546
		if(parameter != null) {
547
			return parameter;
548
		} else {
549
			throw new IllegalArgumentException("Non obsolete paramater was assigned a null value: " + parameter);
550
		}
551
	}
552
	
553
	
554
	/**
555
	 * Returns a list of all datasources stored in the datasource config file
556
	 * @return all existing data sources
557
	 */
558
	@SuppressWarnings("unchecked")
559
	static public List<CdmPersistentDataSource> getAllDataSources(){
560
		List<CdmPersistentDataSource> dataSources = new ArrayList<CdmPersistentDataSource>();
561
		
562
		Element root = getBeansRoot(CdmPersistentSourceUtils.getCdmSourceInputStream());
563
		if (root == null){
564
			return null;
565
		}else{
566
	    	List<Element> lsChildren  = root.getChildren("bean", root.getNamespace());
567
	    	
568
	    	for (Element elBean : lsChildren){
569
	    		String strId = elBean.getAttributeValue("id");
570
	    		if (strId != null && strId.endsWith(DATASOURCE_BEAN_POSTFIX)){
571
	    			strId = strId.replace(DATASOURCE_BEAN_POSTFIX, "");
572
	    			dataSources.add(new CdmPersistentDataSource(strId));
573
	    		}
574
	    	}
575
		}
576
		return dataSources;
577
	}
578
	
579
	
580
	@Override
581
	public boolean equals(Object obj){
582
		if (obj == null){
583
			return false;
584
		}else if (! CdmPersistentDataSource.class.isAssignableFrom(obj.getClass())){
585
			return false;
586
		}else{
587
			CdmPersistentDataSource dataSource = (CdmPersistentDataSource)obj;
588
			return (getName() == dataSource.getName());
589
		}
590

    
591
	}
592
	
593
	@Override
594
	public String toString(){
595
		if (getName() != null){
596
			return getName();
597
		}else{
598
			return null;
599
		}
600
	}
601

    
602

    
603

    
604

    
605

    
606

    
607

    
608

    
609

    
610
}
(3-3/20)