Project

General

Profile

Download (18 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.CdmUtils;
39
import eu.etaxonomy.cdm.common.XmlHelp;
40
import eu.etaxonomy.cdm.config.CdmPersistentSourceUtils;
41
import eu.etaxonomy.cdm.config.CdmPersistentXMLSource;
42
import eu.etaxonomy.cdm.config.CdmPersistentXMLSource.CdmSourceProperties;
43
import eu.etaxonomy.cdm.config.ICdmPersistentSource;
44
import eu.etaxonomy.cdm.database.types.IDatabaseType;
45
import eu.etaxonomy.cdm.model.name.NomenclaturalCode;
46

    
47

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

    
56
		
57
	private String beanName;
58
	private String dbUrl;
59
	private Properties cdmSourceProperties;
60
	private Properties dataSourceProperties;
61
	private List<Attribute> cdmSourceAttributes;
62

    
63

    
64
	/**
65
	 * The Datasource class that Spring will use to set up the connection to the database
66
	 */
67
	private static String dataSourceClassName = ComboPooledDataSource.class.getName();	
68

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

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

    
157

    
158
	@Override
159
	public String getFilePath() {		
160
		return getCdmSourceProperty(CdmSourceProperties.FILEPATH);
161
	}
162

    
163

    
164
	@Override
165
	public H2Mode getMode() {		
166
		return H2Mode.fromString(getCdmSourceProperty(CdmSourceProperties.MODE));
167
	}
168
	
169
	@Override
170
	public String getUsername(){
171
		return getCdmSourceProperty(CdmSourceProperties.USERNAME);
172
	}
173
	
174
	@Override
175
	public String getPassword(){
176
		return getCdmSourceProperty(CdmSourceProperties.PASSWORD);
177
	}
178

    
179
	@Override
180
	public NomenclaturalCode getNomenclaturalCode() {
181
		// TODO null
182
		return NomenclaturalCode.fromString(getCdmSourceProperty(CdmSourceProperties.NOMENCLATURAL_CODE));
183
	}
184

    
185
	@Override
186
	public int getPort() {
187
		String port = CdmUtils.Nz(getCdmSourceProperty(CdmSourceProperties.PORT));
188
		if (port == null || "".equals(port)){
189
			return -1;
190
		}else{
191
			//TODO exception if non integer
192
			return Integer.valueOf(port);
193
		}
194
	}
195

    
196

    
197
	@Override
198
	public String getServer() {
199
		return getCdmSourceProperty(CdmSourceProperties.SERVER);
200
	}
201

    
202

    
203
	@Override
204
	public DatabaseTypeEnum getDatabaseType(){
205
		String strDriverClass = getCdmSourceProperty(CdmSourceProperties.DRIVER_CLASS);
206
		DatabaseTypeEnum dbType = DatabaseTypeEnum.getDatabaseEnumByDriverClass(strDriverClass);
207
		return dbType;
208
	}
209
	
210
	
211

    
212
	
213
	public String getCdmSourceProperty(CdmSourceProperties property){		
214
		return dataSourceProperties.getProperty(property.toString(),null);
215
	}
216
	/**
217
	 * Returns a BeanDefinition object of type DataSource that contains
218
	 * datsource properties (url, username, password, ...)
219
	 * @return
220
	 */
221
	@SuppressWarnings("unchecked")
222
	@Override
223
	public BeanDefinition getDatasourceBean(){
224
		DatabaseTypeEnum dbtype = 
225
				DatabaseTypeEnum.getDatabaseEnumByDriverClass(getCdmSourceProperty(CdmSourceProperties.DRIVER_CLASS));
226
		
227
		AbstractBeanDefinition bd = new RootBeanDefinition(dbtype.getDataSourceClass());
228
		//attributes
229
		Iterator<Attribute> iterator = cdmSourceAttributes.iterator();
230
		while(iterator.hasNext()){
231
			Attribute attribute = iterator.next();
232
			if (attribute.getName().equals("lazy-init")){
233
				bd.setLazyInit(Boolean.valueOf(attribute.getValue()));
234
			}
235
			if (attribute.getName().equals("init-method")){
236
				bd.setInitMethodName(attribute.getValue());
237
			}
238
			if (attribute.getName().equals("destroy-method")){
239
				bd.setDestroyMethodName(attribute.getValue());
240
			}
241
			//Attribute attribute = iterator.next();
242
			//bd.setAttribute(attribute.getName(), attribute.getValue());
243
		}
244
		
245
		//properties
246
		MutablePropertyValues props = new MutablePropertyValues();
247
		
248
		Enumeration<String> keys = (Enumeration)cdmSourceProperties.keys(); 
249
		while (keys.hasMoreElements()){
250
			String key = (String)keys.nextElement();
251
			
252
			if (key.equals("nomenclaturalCode") && cdmSourceProperties.getProperty(key).equals("ICBN")){
253
				//bugfix for old nomenclatural codes, remove if fixed elsewhere, see https://dev.e-taxonomy.eu/trac/ticket/3658
254
				props.addPropertyValue(key, NomenclaturalCode.ICNAFP.name());
255
			}else{
256
				props.addPropertyValue(key, cdmSourceProperties.getProperty(key));
257
			}
258
		}
259

    
260
		bd.setPropertyValues(props);
261
		return bd;
262
	}
263
	
264
	/**
265
	 * @param hbm2dll
266
	 * @param showSql
267
	 * @return
268
	 */
269
	@Override
270
	public BeanDefinition getHibernatePropertiesBean(DbSchemaValidation hbm2dll){
271
		boolean showSql = false;
272
		boolean formatSql = false;
273
		boolean registerSearchListener = false;
274
		Class<? extends RegionFactory> cacheProviderClass = NoCachingRegionFactory.class;
275
		return getHibernatePropertiesBean(hbm2dll, showSql, formatSql, registerSearchListener, cacheProviderClass);
276
	}
277
	
278
	
279
	/**
280
	 * @param hbm2dll
281
	 * @param showSql
282
	 * @return
283
	 */
284
	@Override
285
	public BeanDefinition getHibernatePropertiesBean(DbSchemaValidation hbm2dll, Boolean showSql, Boolean formatSql, Boolean registerSearchListener, Class<? extends RegionFactory> cacheProviderClass){
286
		//Hibernate default values
287
		if (hbm2dll == null){
288
			hbm2dll = DbSchemaValidation.VALIDATE;
289
		}
290
		if (showSql == null){
291
			showSql = false;
292
		}
293
		if (formatSql == null){
294
			formatSql = false;
295
		}
296
		if (cacheProviderClass == null){
297
			cacheProviderClass = NoCachingRegionFactory.class;
298
		}
299
		if(registerSearchListener == null){
300
			registerSearchListener = false;
301
		}
302
				
303
		DatabaseTypeEnum dbtype = getDatabaseType();
304
		AbstractBeanDefinition bd = new RootBeanDefinition(PropertiesFactoryBean.class);
305
		MutablePropertyValues hibernateProps = new MutablePropertyValues();
306

    
307
		Properties props = new Properties();
308
		props.setProperty("hibernate.hbm2ddl.auto", hbm2dll.toString());
309
		props.setProperty("hibernate.dialect", dbtype.getHibernateDialectCanonicalName());
310
		props.setProperty("hibernate.cache.region.factory_class", cacheProviderClass.getName());
311
		props.setProperty("hibernate.show_sql", String.valueOf(showSql));
312
		props.setProperty("hibernate.format_sql", String.valueOf(formatSql));
313
		props.setProperty("hibernate.search.autoregister_listeners", String.valueOf(registerSearchListener));
314

    
315
		hibernateProps.addPropertyValue("properties",props);
316
		bd.setPropertyValues(hibernateProps);
317
		return bd;
318
	}
319
	
320
	
321
	/**
322
	 * Tests existing of the datsource in the according config  file.
323
	 * @return true if a datasource with the given name exists in the according datasource config file.
324
	 */
325
	public static boolean exists(String strDataSourceName){
326
		Element bean = CdmPersistentSourceUtils.getCdmSourceBeanXml(strDataSourceName, DATASOURCE_BEAN_POSTFIX);
327
		return (bean != null);
328
	}
329

    
330
	
331
	/**
332
	 * 
333
	 * @param strDataSourceName
334
	 * @param databaseTypeEnum
335
	 * @param server
336
	 * @param database
337
	 * @param port
338
	 * @param username
339
	 * @param password
340
	 * @param dataSourceClass
341
	 * @param initMethod
342
	 * @param destroyMethod
343
	 * @param startSilent
344
	 * @param startServer
345
	 * @param filePath
346
	 * @param mode
347
	 * @return
348
	 */
349
	private static CdmPersistentDataSource save(String strDataSourceName, 
350
			DatabaseTypeEnum databaseTypeEnum, 
351
			String server, 
352
			String database, 
353
			String port, 
354
			String username, 
355
			String password, 
356
			Class<? extends DataSource> dataSourceClass,
357
			String initMethod,
358
			String destroyMethod,
359
			Boolean startSilent,
360
			Boolean startServer, 
361
			String filePath,
362
			H2Mode mode,
363
			NomenclaturalCode code
364
		){
365
		
366
		int portNumber = "".equals(port) ? databaseTypeEnum.getDefaultPort() : Integer.valueOf(port);
367
		
368
		ICdmDataSource dataSource = new CdmDataSource(databaseTypeEnum, server, database, portNumber, username, password, filePath, mode, code);
369
				
370
		//root
371
		Element root = getBeansRoot(CdmPersistentSourceUtils.getCdmSourceInputStream());
372
		if (root == null){
373
			return null;
374
		}
375
		//bean
376
		Element bean = XmlHelp.getFirstAttributedChild(root, "bean", "id", CdmPersistentSourceUtils.getBeanName(strDataSourceName, DATASOURCE_BEAN_POSTFIX));
377
		if (bean != null){
378
			bean.detach();  //delete old version if necessary
379
		}
380
		bean = insertXmlBean(root, CdmPersistentSourceUtils.getBeanName(strDataSourceName, DATASOURCE_BEAN_POSTFIX), dataSourceClass.getName());
381
		//attributes
382
		bean.setAttribute("lazy-init", "true");
383
		if (initMethod != null) {bean.setAttribute("init-method", initMethod);}
384
		if (destroyMethod != null) {bean.setAttribute("destroy-method", destroyMethod);}
385
		
386
		//set properties
387
		insertXmlValueProperty(bean, "driverClassName", databaseTypeEnum.getDriverClassName());
388
		
389
		insertXmlValueProperty(bean, "url", databaseTypeEnum.getConnectionString(dataSource));
390
		if (username != null) {insertXmlValueProperty(bean, "username", username );}
391
		if (password != null) {insertXmlValueProperty(bean, "password", password );}
392
		if (startSilent != null) {insertXmlValueProperty(bean, "startSilent", startSilent.toString() );}
393
		if (startServer != null) {insertXmlValueProperty(bean, "startServer", startServer.toString() );}
394
		if (filePath != null) {insertXmlValueProperty(bean, "filePath", filePath );}
395
		if (mode != null) {insertXmlValueProperty(bean, "mode", mode.toString() );}
396
		if (code != null) {insertXmlValueProperty(bean, "nomenclaturalCode", code.name());}
397
		
398
		//save
399
		saveToXml(root.getDocument(), 
400
				CdmPersistentSourceUtils.getResourceDirectory(), 
401
				CdmPersistentXMLSource.CDMSOURCE_FILE_NAME, 
402
				XmlHelp.prettyFormat );
403
		try {
404
			return NewInstance(strDataSourceName) ;
405
		} catch (DataSourceNotFoundException e) {
406
			logger.error("Error when saving datasource");
407
			return null;
408
		}
409
	}
410
	
411
	/**
412
	 * @param strDataSourceName
413
	 * @param dataSource
414
	 * @param code 
415
	 * @return
416
	 * 			the updated dataSource, null if not succesful
417
	 */
418
	public static CdmPersistentDataSource update(String strDataSourceName,
419
			ICdmDataSource dataSource) throws DataSourceNotFoundException, IllegalArgumentException{
420
		CdmPersistentSourceUtils.delete(CdmPersistentSourceUtils.getBeanName(strDataSourceName,DATASOURCE_BEAN_POSTFIX));
421
		return save(strDataSourceName, dataSource);
422
	}
423

    
424
	/**
425
	 * Saves a datasource to the datasource config file. If strDataSourceName differs a new dataSource
426
	 * will be created in config file. Use update() of real update functionality.
427
	 * 
428
	 * @param strDataSourceName
429
	 * @param dataSource
430
	 * @return
431
	 */
432
	public static CdmPersistentDataSource save(String strDataSourceName,
433
			ICdmDataSource dataSource)  throws IllegalArgumentException{
434
		
435
		if(dataSource.getDatabaseType() == null){
436
			new IllegalArgumentException("Database type not specified");
437
		}
438
		
439
		if(dataSource.getDatabaseType().equals(DatabaseTypeEnum.H2)){
440
			Class<? extends DataSource> dataSourceClass =  LocalH2.class;
441
			if(dataSource.getMode() == null) {
442
				new IllegalArgumentException("H2 mode not specified");
443
			}			
444
			return save(
445
					strDataSourceName, 
446
					dataSource.getDatabaseType(), 
447
					"localhost", 
448
					getCheckedDataSourceParameter(dataSource.getDatabase()), 
449
					dataSource.getDatabaseType().getDefaultPort() + "", 
450
					getCheckedDataSourceParameter(dataSource.getUsername()), 
451
					getCheckedDataSourceParameter(dataSource.getPassword()), 
452
					dataSourceClass, 
453
					null, null, null, null, 
454
					getCheckedDataSourceParameter(dataSource.getFilePath()), 
455
					dataSource.getMode(),
456
					dataSource.getNomenclaturalCode());
457
		}else{
458
			
459
			Class<? extends DataSource> dataSourceClass;
460
			try {
461
				dataSourceClass = (Class<? extends DataSource>) Class.forName(dataSourceClassName);
462
				
463
				CdmPersistentDataSource persistendDatasource =  save(
464
					strDataSourceName, 
465
					dataSource.getDatabaseType(), 
466
					getCheckedDataSourceParameter(dataSource.getServer()), 
467
					getCheckedDataSourceParameter(dataSource.getDatabase()), 
468
					dataSource.getPort() + "", 
469
					getCheckedDataSourceParameter(dataSource.getUsername()), 
470
					getCheckedDataSourceParameter(dataSource.getPassword()), 
471
					dataSourceClass, 
472
					null, null, null, null, null, null,
473
					dataSource.getNomenclaturalCode());
474
				
475
				return persistendDatasource;
476
			} catch (ClassNotFoundException e) {
477
				logger.error("DataSourceClass not found - stopping application", e);
478
				System.exit(-1);
479
			}
480
			// will never be reached
481
			return null;
482
		}
483
	}
484

    
485
	private static String getCheckedDataSourceParameter(String parameter) throws IllegalArgumentException{		
486
		if(parameter != null) {
487
			return parameter;
488
		} else {
489
			throw new IllegalArgumentException("Non obsolete paramater was assigned a null value: " + parameter);
490
		}
491
	}
492
	
493
	
494
	/**
495
	 * Returns a list of all datasources stored in the datasource config file
496
	 * @return all existing data sources
497
	 */
498
	@SuppressWarnings("unchecked")
499
	static public List<CdmPersistentDataSource> getAllDataSources(){
500
		List<CdmPersistentDataSource> dataSources = new ArrayList<CdmPersistentDataSource>();
501
		
502
		Element root = getBeansRoot(CdmPersistentSourceUtils.getCdmSourceInputStream());
503
		if (root == null){
504
			return null;
505
		}else{
506
	    	List<Element> lsChildren  = root.getChildren("bean", root.getNamespace());
507
	    	
508
	    	for (Element elBean : lsChildren){
509
	    		String strId = elBean.getAttributeValue("id");
510
	    		if (strId != null && strId.endsWith(DATASOURCE_BEAN_POSTFIX)){
511
	    			strId = strId.replace(DATASOURCE_BEAN_POSTFIX, "");
512
	    			dataSources.add(new CdmPersistentDataSource(strId));
513
	    		}
514
	    	}
515
		}
516
		return dataSources;
517
	}
518
	
519
	
520
	@Override
521
	public boolean equals(Object obj){
522
		if (obj == null){
523
			return false;
524
		}else if (! CdmPersistentDataSource.class.isAssignableFrom(obj.getClass())){
525
			return false;
526
		}else{
527
			CdmPersistentDataSource dataSource = (CdmPersistentDataSource)obj;
528
			return (getName() == dataSource.getName());
529
		}
530

    
531
	}
532
	
533
	@Override
534
	public String toString(){
535
		if (getName() != null){
536
			return getName();
537
		}else{
538
			return null;
539
		}
540
	}
541

    
542

    
543

    
544

    
545
}
(3-3/20)