Project

General

Profile

Download (17.9 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 java.io.File;
13
import java.io.FileInputStream;
14
import java.io.FileNotFoundException;
15
import java.io.FileOutputStream;
16
import java.io.FilenameFilter;
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 org.apache.log4j.Logger;
24
import org.hibernate.cache.CacheProvider;
25
import org.hibernate.cache.NoCacheProvider;
26
import org.jdom.Attribute;
27
import org.jdom.Document;
28
import org.jdom.Element;
29
import org.jdom.output.Format;
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
import org.springframework.jdbc.datasource.DriverManagerDataSource;
36

    
37
import eu.etaxonomy.cdm.api.application.CdmApplicationUtils;
38
import eu.etaxonomy.cdm.common.XmlHelp;
39

    
40
import static eu.etaxonomy.cdm.common.XmlHelp.getRoot;
41
import static eu.etaxonomy.cdm.common.XmlHelp.insertXmlBean;
42
import static eu.etaxonomy.cdm.common.XmlHelp.insertXmlValueProperty;
43
import static eu.etaxonomy.cdm.common.XmlHelp.saveToXml;
44

    
45

    
46
/**
47
 * class to access an CdmDataSource
48
 */
49
public class CdmPersistentDataSource implements ICdmDataSource {
50
	private static final Logger logger = Logger.getLogger(CdmPersistentDataSource.class);
51
	
52
	public static final String DATASOURCE_BEAN_POSTFIX = "DataSource";
53
	public final static String DATASOURCE_FILE_NAME = "cdm.datasources.xml";
54
	private final static Format format = Format.getPrettyFormat(); 
55

    
56
	public enum DbProperties{
57
		DRIVER_CLASS,
58
		URL,
59
		USERNAME,
60
		PASSWORD;
61

    
62
		@Override
63
		public String toString(){
64
			switch (this){
65
				case DRIVER_CLASS:
66
					return "driverClassName";
67
				case URL:
68
					return "url";
69
				case USERNAME:
70
					return "username";
71
				case PASSWORD:
72
					return "password";
73
				default: 
74
					throw new IllegalArgumentException( "Unknown enumeration type" );
75
			}
76
		}
77
	}
78
	
79
	//name
80
	protected String dataSourceName;
81

    
82
	
83
	/**
84
	 * Returns the default CdmDataSource
85
	 * @return the default CdmDataSource
86
	 */
87
	public final static CdmPersistentDataSource NewDefaultInstance(){
88
		try {
89
			return NewInstance("default");
90
		} catch (DataSourceNotFoundException e) {
91
			logger.error("Default datasource does not exist in config file");
92
			return null;
93
		}
94
	}
95
	
96
	
97
	/**
98
	 * Returns the default CdmDataSource
99
	 * @return the default CdmDataSource
100
	 */
101
	public final static CdmPersistentDataSource NewLocalHsqlInstance(){
102
		try {
103
			return NewInstance("localDefaultHsql");
104
		} catch (DataSourceNotFoundException e) {
105
			logger.error("Local datasource does not exist in config file");
106
			return null;
107
		}
108
	}
109
	
110
	/**
111
	 * Returns the CdmDataSource named by strDataSource
112
	 * @param strDataSource
113
	 * @return
114
	 */
115
	public final static CdmPersistentDataSource NewInstance(String dataSourceName) 
116
				throws DataSourceNotFoundException{
117
		if (exists(dataSourceName)){
118
			return new CdmPersistentDataSource(dataSourceName);
119
		}else{
120
			throw new DataSourceNotFoundException("Datasource not found: " + dataSourceName);
121
		}
122
	}
123

    
124
	/**
125
	 * Private Constructor. Use NewXXX factory methods for creating a new instance of CdmDataSource!
126
	 * @param strDataSource
127
	 */
128
	private CdmPersistentDataSource(String strDataSource){
129
		dataSourceName = strDataSource;
130
	}
131
	
132
	/**
133
	 * Returns the name of the bean.
134
	 * @return
135
	 */
136
	public String getName(){
137
		return dataSourceName;
138
	}
139
	
140
	
141
	/**
142
	 * Returns the name of the bean Element in the xml config file.
143
	 * @return bean name
144
	 */
145
	private static String getBeanName(String name){
146
		return name == null? null : name + DATASOURCE_BEAN_POSTFIX;
147
	}
148

    
149

    
150
	
151
	/**
152
	 * Returns the database type of the data source. 
153
	 * @return the database type of the data source. Null if the bean or the driver class property does not exist or the driver class is unknown.
154
	 */
155
	public DatabaseTypeEnum getDatabaseType(){
156
		Element bean = getDatasourceBeanXml(this.dataSourceName);
157
		if (bean == null){
158
			return null;
159
		}else{
160
			Element driverProp = XmlHelp.getFirstAttributedChild(bean, "property", "name", "driverClassName");
161
			if (driverProp == null){
162
				logger.warn("Unknown property driverClass");
163
		    	return null;
164
			}else{
165
				String strDriverClass = driverProp.getAttributeValue("value");
166
				DatabaseTypeEnum dbType = DatabaseTypeEnum.getDatabaseEnumByDriverClass(strDriverClass);
167
				return dbType;
168
			}
169
		}
170
	}
171

    
172

    
173
	/**
174
	 * Returns the list of properties that are defined in the datasource    
175
	 * @return 
176
	 */
177
	public List<Attribute> getDatasourceAttributes(){
178
		List<Attribute> result = new ArrayList<Attribute>();
179
		Element bean = getDatasourceBeanXml(this.dataSourceName);
180
		if (bean == null){
181
			return null;
182
		}else{
183
			result = bean.getAttributes();
184
		}
185
		return result;
186
	}	
187

    
188
	/**
189
	 * Returns a defined property of the datasource
190
	 * @return the property of the data source. NULL if the datasource bean or the property does not exist.
191
	 */
192
	public String getDatasourceProperty(DbProperties dbProp){
193
		Element bean = getDatasourceBeanXml(this.dataSourceName);
194
		if (bean == null){
195
			return null;
196
		}else{
197
			Element elProperty = XmlHelp.getFirstAttributedChild(bean, "property", "name", dbProp.toString());
198
			if (elProperty == null){
199
				logger.warn("Unknown property: " + dbProp.toString());
200
		    	return null;
201
			}else{
202
				String strValue = elProperty.getAttributeValue("value");
203
				return strValue;
204
			}
205
		}
206
	}
207

    
208
	
209
	/**
210
	 * Returns the list of properties that are defined in the datasource    
211
	 * @return 
212
	 */
213
	public Properties getDatasourceProperties(){
214
		Properties result = new Properties();
215
		Element bean = getDatasourceBeanXml(this.dataSourceName);
216
		if (bean == null){
217
			return null;
218
		}else{
219
			List<Element> elProperties = XmlHelp.getAttributedChildList(bean, "property", "name");
220
			Iterator<Element> iterator = elProperties.iterator();
221
			while(iterator.hasNext()){
222
				Element next = iterator.next();
223
				String strName = next.getAttributeValue("name");
224
				String strValue = next.getAttributeValue("value");
225
				result.put(strName, strValue);
226
			}
227
		}
228
		return result;
229
	}
230
	
231
	/**
232
	 * Returns a BeanDefinition object of type  DriverManagerDataSource that contains
233
	 * datsource properties (url, username, password, ...)
234
	 * @return
235
	 */
236
	public BeanDefinition getDatasourceBean(){
237
		DatabaseTypeEnum dbtype = DatabaseTypeEnum.getDatabaseEnumByDriverClass(getDatasourceProperty(DbProperties.DRIVER_CLASS));
238
		
239
		AbstractBeanDefinition bd = new RootBeanDefinition(dbtype.getDriverManagerDataSourceClass());
240
		//attributes
241
		Iterator<Attribute> iterator = getDatasourceAttributes().iterator();
242
		while(iterator.hasNext()){
243
			Attribute attribute = iterator.next();
244
			if (attribute.getName().equals("lazy-init")){
245
				bd.setLazyInit(Boolean.valueOf(attribute.getValue()));
246
			}
247
			if (attribute.getName().equals("init-method")){
248
				bd.setInitMethodName(attribute.getValue());
249
			}
250
			if (attribute.getName().equals("destroy-method")){
251
				bd.setDestroyMethodName(attribute.getValue());
252
			}
253
			//Attribute attribute = iterator.next();
254
			//bd.setAttribute(attribute.getName(), attribute.getValue());
255
		}
256
		
257
		//properties
258
		MutablePropertyValues props = new MutablePropertyValues();
259
		Properties persistentProperties = getDatasourceProperties();
260
		Enumeration<String> keys = (Enumeration)persistentProperties.keys(); 
261
		while (keys.hasMoreElements()){
262
			String key = (String)keys.nextElement();
263
			props.addPropertyValue(key, persistentProperties.getProperty(key));
264
		}
265

    
266
		bd.setPropertyValues(props);
267
		return bd;
268
	}
269
	
270
	/**
271
	 * @param hbm2dll
272
	 * @param showSql
273
	 * @return
274
	 */
275
	public BeanDefinition getHibernatePropertiesBean(DbSchemaValidation hbm2dll){
276
		boolean showSql = false;
277
		boolean formatSql = false;
278
		Class<? extends CacheProvider> cacheProviderClass = NoCacheProvider.class;
279
		return getHibernatePropertiesBean(hbm2dll, showSql, formatSql, cacheProviderClass);
280
	}
281
	
282
	
283
	/**
284
	 * @param hbm2dll
285
	 * @param showSql
286
	 * @return
287
	 */
288
	public BeanDefinition getHibernatePropertiesBean(DbSchemaValidation hbm2dll, Boolean showSql, Boolean formatSql, Class<? extends CacheProvider> cacheProviderClass){
289
		//Hibernate default values
290
		if (hbm2dll == null){
291
			hbm2dll = DbSchemaValidation.VALIDATE;
292
		}
293
		if (showSql == null){
294
			showSql = false;
295
		}
296
		if (formatSql == null){
297
			formatSql = false;
298
		}
299
		if (cacheProviderClass == null){
300
			cacheProviderClass = NoCacheProvider.class;
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.getHibernateDialect());
310
		props.setProperty("hibernate.cache.provider_class", cacheProviderClass.getName());
311
		props.setProperty("hibernate.show_sql", String.valueOf(showSql));
312
		props.setProperty("hibernate.format_sql", String.valueOf(formatSql));
313

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

    
329
	
330
	/**
331
	 * Saves or updates the datasource to the datasource config file.
332
	 * Uses default port.
333
	 * @param strDataSourceName name of the datasource (without postfix DataSource)
334
	 * @param databaseTypeEnum
335
	 * @param server
336
	 * @param database
337
	 * @param username
338
	 * @param password
339
	 * @return the CdmDataSource, null if not successful.
340
	 */
341
	public static CdmPersistentDataSource save(String strDataSourceName, DatabaseTypeEnum databaseTypeEnum, String server, String database, 
342
			String username, String password){
343
		return save(strDataSourceName, databaseTypeEnum, server, database, 
344
				databaseTypeEnum.getDefaultPort(), username, password);
345
	}
346
	
347
	/**
348
	 * Saves or updates the datasource to the datasource config file.
349
	 * @param strDataSourceName name of the datasource (without postfix DataSource)
350
	 * @param databaseTypeEnum
351
	 * @param server
352
	 * @param database
353
	 * @param port
354
	 * @param username
355
	 * @param password
356
	 * @return the CdmDataSource, null if not successful.
357
	 */
358
	public static CdmPersistentDataSource save(String strDataSourceName, DatabaseTypeEnum databaseTypeEnum, String server, String database, 
359
				int port, String username, String password){
360
		Class<? extends DriverManagerDataSource> driverManagerDataSource =  DriverManagerDataSource.class;
361
		return save(strDataSourceName, databaseTypeEnum, server, database, port, username, password, driverManagerDataSource, null, null, null, null, null);
362
	}
363
	
364
	
365
	public static CdmPersistentDataSource saveLocalHsqlDb(String strDataSourceName, String databasePath, String databaseName, String username, String password){
366
		DatabaseTypeEnum databaseTypeEnum = DatabaseTypeEnum.HSqlDb;
367
		Class<? extends DriverManagerDataSource> driverManagerDataSource =  LocalHsqldb.class;
368
		String server = "localhost";
369
		int port = databaseTypeEnum.getDefaultPort();
370
		return save(strDataSourceName, databaseTypeEnum, server, databaseName, port, username, password, driverManagerDataSource, "init", "destroy", true, true, databasePath);
371
	}
372
	
373
	//
374
	private static CdmPersistentDataSource save(String strDataSourceName, 
375
			DatabaseTypeEnum databaseTypeEnum, 
376
			String server, 
377
			String database, 
378
			int port, 
379
			String username, 
380
			String password, 
381
			Class<? extends DriverManagerDataSource> driverManagerDataSource,
382
			String initMethod,
383
			String destroyMethod,
384
			Boolean startSilent,
385
			Boolean startServer, 
386
			String databasePath
387
		){
388
		//root
389
		Element root = getRoot(getDataSourceInputStream());
390
		if (root == null){
391
			return null;
392
		}
393
		//bean
394
		Element bean = XmlHelp.getFirstAttributedChild(root, "bean", "id", getBeanName(strDataSourceName));
395
		if (bean != null){
396
			bean.detach();  //delete old version if necessary
397
		}
398
		bean = insertXmlBean(root, getBeanName(strDataSourceName), driverManagerDataSource.getName());
399
		//attributes
400
		bean.setAttribute("lazy-init", "true");
401
		if (initMethod != null) {bean.setAttribute("init-method", initMethod);}
402
		if (destroyMethod != null) {bean.setAttribute("destroy-method", destroyMethod);}
403
		
404
		//set properties
405
		insertXmlValueProperty(bean, "driverClassName", databaseTypeEnum.getDriverClassName());
406
		insertXmlValueProperty(bean, "url", databaseTypeEnum.getConnectionString(server, database, port));
407
		if (username != null) {insertXmlValueProperty(bean, "username", username );}
408
		if (password != null) {insertXmlValueProperty(bean, "password", password );}
409
		if (startSilent != null) {insertXmlValueProperty(bean, "startSilent", startSilent.toString() );}
410
		if (startServer != null) {insertXmlValueProperty(bean, "startServer", startServer.toString() );}
411
		if (startServer != null) {insertXmlValueProperty(bean, "databasePath", databasePath );}
412
		
413
		//save
414
		saveToXml(root.getDocument(), getResourceDirectory(), DATASOURCE_FILE_NAME, format );
415
		try {
416
			return NewInstance(strDataSourceName) ;
417
		} catch (DataSourceNotFoundException e) {
418
			logger.error("Error when saving datasource");
419
			return null;
420
		}
421
	}
422
	
423
	
424
	/**
425
	 * Deletes a dataSource
426
	 * @param dataSource
427
	 */
428
	public static void delete (CdmPersistentDataSource dataSource){
429
		Element bean = getDatasourceBeanXml(dataSource.getName());
430
		if (bean != null){
431
			Document doc = bean.getDocument();
432
			bean.detach();
433
			saveToXml(doc, getDataSourceOutputStream(), format );
434
		}
435
	}
436
	
437
	
438
	/**
439
	 * Returns a list of all datasources stored in the datasource config file
440
	 * @return all existing data sources
441
	 */
442
	static public List<CdmPersistentDataSource> getAllDataSources(){
443
		List<CdmPersistentDataSource> dataSources = new ArrayList<CdmPersistentDataSource>();
444
		
445
		Element root = getRoot(getDataSourceInputStream());
446
		if (root == null){
447
			return null;
448
		}else{
449
	    	List<Element> lsChildren  = root.getChildren("bean", root.getNamespace());
450
	    	
451
	    	for (Element elBean : lsChildren){
452
	    		String strId = elBean.getAttributeValue("id");
453
	    		if (strId != null && strId.endsWith(DATASOURCE_BEAN_POSTFIX)){
454
	    			strId = strId.replace(DATASOURCE_BEAN_POSTFIX, "");
455
	    			dataSources.add(new CdmPersistentDataSource(strId));
456
	    		}
457
	    	}
458
		}
459
		return dataSources;
460
	}
461
	
462
	
463
	/* (non-Javadoc)
464
	 * @see java.lang.Object#toString()
465
	 */
466
	public String toString(){
467
		if (this.dataSourceName != null){
468
			return dataSourceName;
469
		}else{
470
			return null;
471
		}
472
	}
473

    
474

    
475
	
476
	/**
477
	 * Returns the datasource config file input stream.
478
	 * @return data source config file input stream
479
	 */
480
	static protected FileInputStream getDataSourceInputStream(){
481
		String dir = getResourceDirectory();
482
		File file = new File(dir + File.separator +  DATASOURCE_FILE_NAME);
483
		return fileInputStream(file);
484
	}
485
	
486
	
487
	/**
488
	 * Returns the datasource config file outputStream.
489
	 * @return data source config file outputStream
490
	 */
491
	static protected FileOutputStream getDataSourceOutputStream(){
492
		String dir = getResourceDirectory();
493
		File file = new File(dir + File.separator +  DATASOURCE_FILE_NAME);
494
		return fileOutputStream(file);
495
	}
496

    
497
	/**
498
	 * Returns the jdom Element representing the data source bean in the config file.
499
	 * @return
500
	 */
501
	private static Element getDatasourceBeanXml(String strDataSourceName){
502
		FileInputStream inStream = getDataSourceInputStream();
503
		Element root = getRoot(inStream);
504
		if (root == null){
505
			return null;
506
		}else{
507
	    	Element xmlBean = XmlHelp.getFirstAttributedChild(root, "bean", "id", getBeanName(strDataSourceName));
508
			if (xmlBean == null){
509
				//TODO warn or info
510
				logger.debug("Unknown Element 'bean id=" +strDataSourceName + "' ");
511
			};
512
			return xmlBean;
513
		}
514
	}
515
	
516
	// returns the directory containing the resources 
517
	private static String getResourceDirectory(){
518
		File f = CdmApplicationUtils.getWritableResourceDir();
519
		return f.getPath();
520
	}
521
	
522
	static private FileInputStream fileInputStream(File file){
523
		try {
524
			FileInputStream fis = new FileInputStream(file);
525
			return fis;
526
		} catch (FileNotFoundException e) {
527
			logger.warn("File " + file == null?"null":file.getAbsolutePath() + " does not exist in the file system");
528
			return null;
529
		}
530
	}
531
	
532
	static private FileOutputStream fileOutputStream(File file){
533
		try {
534
			FileOutputStream fos = new FileOutputStream(file);
535
			return fos;
536
		} catch (FileNotFoundException e) {
537
			logger.warn("File " + (file == null?"null":file.getAbsolutePath()) + " does not exist in the file system");
538
			return null;
539
		}
540
	}
541
	
542
	
543
	/**
544
	 * Filter class to define datasource file format
545
	 */
546
	private static class DataSourceFileNameFilter implements FilenameFilter{
547
		public boolean accept(File dir, String name) {
548
	        return (name.endsWith(DATASOURCE_FILE_NAME));
549
	    }
550
	}
551
	
552
	public boolean equals(Object obj){
553
		if (obj == null){
554
			return false;
555
		}else if (! CdmPersistentDataSource.class.isAssignableFrom(obj.getClass())){
556
			return false;
557
		}else{
558
			CdmPersistentDataSource dataSource = (CdmPersistentDataSource)obj;
559
			return (this.dataSourceName == dataSource.dataSourceName);
560
		}
561

    
562
	}
563
}
(2-2/9)