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 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.CdmUtils;
39
import eu.etaxonomy.cdm.common.XmlHelp;
40

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

    
46

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

    
59
	public enum DbProperties{
60
		DRIVER_CLASS,
61
		URL,
62
		USERNAME,
63
		PASSWORD;
64

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

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

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

    
152

    
153
	
154
	public String getDatabase() {
155
		//TODO null
156
		return getDatabaseProperty("database");
157
	}
158

    
159

    
160
	public String getFilePath() {
161
		//TODO null
162
		return getDatabaseProperty("filePath");
163
	}
164

    
165

    
166
	public H2Mode getMode() {
167
		//TODO null
168
		return H2Mode.fromString(getDatabaseProperty("mode"));
169
	}
170

    
171
	public int getPort() {
172
		String port = CdmUtils.Nz(getDatabaseProperty("port"));
173
		if ("".equals(port)){
174
			return -1;
175
		}else{
176
			//TODO exception if non integer
177
			return Integer.getInteger(getDatabaseProperty("port"));
178
		}
179
	}
180

    
181

    
182
	public String getServer() {
183
		//TODO null
184
		return getDatabaseProperty("server");
185
	}
186

    
187
	/**
188
	 * Returns the database type of the data source. 
189
	 * @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.
190
	 */
191
	public DatabaseTypeEnum getDatabaseType(){
192
		Element bean = getDatasourceBeanXml(this.dataSourceName);
193
		if (bean == null){
194
			return null;
195
		}else{
196
			Element driverProp = XmlHelp.getFirstAttributedChild(bean, "property", "name", "driverClassName");
197
			if (driverProp == null){
198
				logger.warn("Unknown property driverClass");
199
		    	return null;
200
			}else{
201
				String strDriverClass = driverProp.getAttributeValue("value");
202
				DatabaseTypeEnum dbType = DatabaseTypeEnum.getDatabaseEnumByDriverClass(strDriverClass);
203
				return dbType;
204
			}
205
		}
206
	}
207
	
208
	
209
	/**
210
	 * Returns the database type of the data source. 
211
	 * @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.
212
	 */
213
	protected String getDatabaseProperty(String property){
214
		Element bean = getDatasourceBeanXml(this.dataSourceName);
215
		if (bean == null){
216
			return null;
217
		}else{
218
			Element driverProp = XmlHelp.getFirstAttributedChild(bean, "property", "name", property);
219
			if (driverProp == null){
220
				logger.warn("Unknown property" + property);
221
		    	return null;
222
			}else{
223
				String strProperty = driverProp.getAttributeValue("value");
224
				return strProperty;
225
			}
226
		}
227
	}
228
	
229

    
230

    
231
	/**
232
	 * Returns the list of properties that are defined in the datasource    
233
	 * @return 
234
	 */
235
	public List<Attribute> getDatasourceAttributes(){
236
		List<Attribute> result = new ArrayList<Attribute>();
237
		Element bean = getDatasourceBeanXml(this.dataSourceName);
238
		if (bean == null){
239
			return null;
240
		}else{
241
			result = bean.getAttributes();
242
		}
243
		return result;
244
	}	
245

    
246
	/**
247
	 * Returns a defined property of the datasource
248
	 * @return the property of the data source. NULL if the datasource bean or the property does not exist.
249
	 */
250
	public String getDatasourceProperty(DbProperties dbProp){
251
		Element bean = getDatasourceBeanXml(this.dataSourceName);
252
		if (bean == null){
253
			return null;
254
		}else{
255
			Element elProperty = XmlHelp.getFirstAttributedChild(bean, "property", "name", dbProp.toString());
256
			if (elProperty == null){
257
				logger.warn("Unknown property: " + dbProp.toString());
258
		    	return null;
259
			}else{
260
				String strValue = elProperty.getAttributeValue("value");
261
				return strValue;
262
			}
263
		}
264
	}
265

    
266
	
267
	/**
268
	 * Returns the list of properties that are defined in the datasource    
269
	 * @return 
270
	 */
271
	public Properties getDatasourceProperties(){
272
		Properties result = new Properties();
273
		Element bean = getDatasourceBeanXml(this.dataSourceName);
274
		if (bean == null){
275
			return null;
276
		}else{
277
			List<Element> elProperties = XmlHelp.getAttributedChildList(bean, "property", "name");
278
			Iterator<Element> iterator = elProperties.iterator();
279
			while(iterator.hasNext()){
280
				Element next = iterator.next();
281
				String strName = next.getAttributeValue("name");
282
				String strValue = next.getAttributeValue("value");
283
				result.put(strName, strValue);
284
			}
285
		}
286
		return result;
287
	}
288
	
289
	/**
290
	 * Returns a BeanDefinition object of type  DriverManagerDataSource that contains
291
	 * datsource properties (url, username, password, ...)
292
	 * @return
293
	 */
294
	public BeanDefinition getDatasourceBean(){
295
		DatabaseTypeEnum dbtype = DatabaseTypeEnum.getDatabaseEnumByDriverClass(getDatasourceProperty(DbProperties.DRIVER_CLASS));
296
		
297
		AbstractBeanDefinition bd = new RootBeanDefinition(dbtype.getDriverManagerDataSourceClass());
298
		//attributes
299
		Iterator<Attribute> iterator = getDatasourceAttributes().iterator();
300
		while(iterator.hasNext()){
301
			Attribute attribute = iterator.next();
302
			if (attribute.getName().equals("lazy-init")){
303
				bd.setLazyInit(Boolean.valueOf(attribute.getValue()));
304
			}
305
			if (attribute.getName().equals("init-method")){
306
				bd.setInitMethodName(attribute.getValue());
307
			}
308
			if (attribute.getName().equals("destroy-method")){
309
				bd.setDestroyMethodName(attribute.getValue());
310
			}
311
			//Attribute attribute = iterator.next();
312
			//bd.setAttribute(attribute.getName(), attribute.getValue());
313
		}
314
		
315
		//properties
316
		MutablePropertyValues props = new MutablePropertyValues();
317
		Properties persistentProperties = getDatasourceProperties();
318
		Enumeration<String> keys = (Enumeration)persistentProperties.keys(); 
319
		while (keys.hasMoreElements()){
320
			String key = (String)keys.nextElement();
321
			props.addPropertyValue(key, persistentProperties.getProperty(key));
322
		}
323

    
324
		bd.setPropertyValues(props);
325
		return bd;
326
	}
327
	
328
	/**
329
	 * @param hbm2dll
330
	 * @param showSql
331
	 * @return
332
	 */
333
	public BeanDefinition getHibernatePropertiesBean(DbSchemaValidation hbm2dll){
334
		boolean showSql = false;
335
		boolean formatSql = false;
336
		Class<? extends CacheProvider> cacheProviderClass = NoCacheProvider.class;
337
		return getHibernatePropertiesBean(hbm2dll, showSql, formatSql, cacheProviderClass);
338
	}
339
	
340
	
341
	/**
342
	 * @param hbm2dll
343
	 * @param showSql
344
	 * @return
345
	 */
346
	public BeanDefinition getHibernatePropertiesBean(DbSchemaValidation hbm2dll, Boolean showSql, Boolean formatSql, Class<? extends CacheProvider> cacheProviderClass){
347
		//Hibernate default values
348
		if (hbm2dll == null){
349
			hbm2dll = DbSchemaValidation.VALIDATE;
350
		}
351
		if (showSql == null){
352
			showSql = false;
353
		}
354
		if (formatSql == null){
355
			formatSql = false;
356
		}
357
		if (cacheProviderClass == null){
358
			cacheProviderClass = NoCacheProvider.class;
359
		}
360
		
361
		DatabaseTypeEnum dbtype = getDatabaseType();
362
		AbstractBeanDefinition bd = new RootBeanDefinition(PropertiesFactoryBean.class);
363
		MutablePropertyValues hibernateProps = new MutablePropertyValues();
364

    
365
		Properties props = new Properties();
366
		props.setProperty("hibernate.hbm2ddl.auto", hbm2dll.toString());
367
		props.setProperty("hibernate.dialect", dbtype.getHibernateDialect());
368
		props.setProperty("hibernate.cache.provider_class", cacheProviderClass.getName());
369
		props.setProperty("hibernate.show_sql", String.valueOf(showSql));
370
		props.setProperty("hibernate.format_sql", String.valueOf(formatSql));
371

    
372
		hibernateProps.addPropertyValue("properties",props);
373
		bd.setPropertyValues(hibernateProps);
374
		return bd;
375
	}
376
	
377
	
378
	/**
379
	 * Tests existing of the datsource in the according config  file.
380
	 * @return true if a datasource with the given name exists in the according datasource config file.
381
	 */
382
	public static boolean exists(String strDataSourceName){
383
		Element bean = getDatasourceBeanXml(strDataSourceName);
384
		return (bean != null);
385
	}
386

    
387
	
388
	/**
389
	 * Saves or updates the datasource to the datasource config file.
390
	 * Uses default port.
391
	 * @param strDataSourceName name of the datasource (without postfix DataSource)
392
	 * @param databaseTypeEnum
393
	 * @param server
394
	 * @param database
395
	 * @param username
396
	 * @param password
397
	 * @return the CdmDataSource, null if not successful.
398
	 */
399
	public static CdmPersistentDataSource save(String strDataSourceName, DatabaseTypeEnum databaseTypeEnum, String server, String database, 
400
			String username, String password){
401
		return save(strDataSourceName, databaseTypeEnum, server, database, 
402
				databaseTypeEnum.getDefaultPort(), username, password);
403
	}
404
	
405
	/**
406
	 * Saves or updates the datasource to the datasource config file.
407
	 * @param strDataSourceName name of the datasource (without postfix DataSource)
408
	 * @param databaseTypeEnum
409
	 * @param server
410
	 * @param database
411
	 * @param port
412
	 * @param username
413
	 * @param password
414
	 * @return the CdmDataSource, null if not successful.
415
	 */
416
	public static CdmPersistentDataSource save(String strDataSourceName, DatabaseTypeEnum databaseTypeEnum, String server, String database, 
417
				int port, String username, String password){
418
		Class<? extends DriverManagerDataSource> driverManagerDataSource =  DriverManagerDataSource.class;
419
		return save(strDataSourceName, databaseTypeEnum, server, database, port, username, password, driverManagerDataSource, null, null, null, null, null, null);
420
	}
421
	
422
	
423
	public static CdmPersistentDataSource saveLocalHsqlDb(String strDataSourceName, String databasePath, String databaseName, String username, String password){
424
		DatabaseTypeEnum databaseTypeEnum = DatabaseTypeEnum.HSqlDb;
425
		Class<? extends DriverManagerDataSource> driverManagerDataSource =  LocalHsqldb.class;
426
		String server = "localhost";
427
		int port = databaseTypeEnum.getDefaultPort();
428
		return save(strDataSourceName, databaseTypeEnum, server, databaseName, port, username, password, driverManagerDataSource, "init", "destroy", true, true, databasePath, null);
429
	}
430
	
431
	//
432
	private static CdmPersistentDataSource save(String strDataSourceName, 
433
			DatabaseTypeEnum databaseTypeEnum, 
434
			String server, 
435
			String database, 
436
			int port, 
437
			String username, 
438
			String password, 
439
			Class<? extends DriverManagerDataSource> driverManagerDataSource,
440
			String initMethod,
441
			String destroyMethod,
442
			Boolean startSilent,
443
			Boolean startServer, 
444
			String filePath,
445
			H2Mode mode
446
		){
447
		ICdmDataSource dataSource = new CdmDataSource(databaseTypeEnum, server, database, port, username, password, filePath, mode);
448
				
449
		//root
450
		Element root = getBeansRoot(getDataSourceInputStream());
451
		if (root == null){
452
			return null;
453
		}
454
		//bean
455
		Element bean = XmlHelp.getFirstAttributedChild(root, "bean", "id", getBeanName(strDataSourceName));
456
		if (bean != null){
457
			bean.detach();  //delete old version if necessary
458
		}
459
		bean = insertXmlBean(root, getBeanName(strDataSourceName), driverManagerDataSource.getName());
460
		//attributes
461
		bean.setAttribute("lazy-init", "true");
462
		if (initMethod != null) {bean.setAttribute("init-method", initMethod);}
463
		if (destroyMethod != null) {bean.setAttribute("destroy-method", destroyMethod);}
464
		
465
		//set properties
466
		insertXmlValueProperty(bean, "driverClassName", databaseTypeEnum.getDriverClassName());
467
		
468
		insertXmlValueProperty(bean, "url", databaseTypeEnum.getConnectionString(dataSource));
469
		if (username != null) {insertXmlValueProperty(bean, "username", username );}
470
		if (password != null) {insertXmlValueProperty(bean, "password", password );}
471
		if (startSilent != null) {insertXmlValueProperty(bean, "startSilent", startSilent.toString() );}
472
		if (startServer != null) {insertXmlValueProperty(bean, "startServer", startServer.toString() );}
473
		if (filePath != null) {insertXmlValueProperty(bean, "filePath", filePath );}
474
		if (mode != null) {insertXmlValueProperty(bean, "mode", mode.toString() );}
475
		
476
		//save
477
		saveToXml(root.getDocument(), getResourceDirectory(), DATASOURCE_FILE_NAME, format );
478
		try {
479
			return NewInstance(strDataSourceName) ;
480
		} catch (DataSourceNotFoundException e) {
481
			logger.error("Error when saving datasource");
482
			return null;
483
		}
484
	}
485
	
486
	
487
	/**
488
	 * Deletes a dataSource
489
	 * @param dataSource
490
	 */
491
	public static void delete (CdmPersistentDataSource dataSource){
492
		Element bean = getDatasourceBeanXml(dataSource.getName());
493
		if (bean != null){
494
			Document doc = bean.getDocument();
495
			bean.detach();
496
			saveToXml(doc, getDataSourceOutputStream(), format );
497
		}
498
	}
499
	
500
	
501
	/**
502
	 * Returns a list of all datasources stored in the datasource config file
503
	 * @return all existing data sources
504
	 */
505
	static public List<CdmPersistentDataSource> getAllDataSources(){
506
		List<CdmPersistentDataSource> dataSources = new ArrayList<CdmPersistentDataSource>();
507
		
508
		Element root = getBeansRoot(getDataSourceInputStream());
509
		if (root == null){
510
			return null;
511
		}else{
512
	    	List<Element> lsChildren  = root.getChildren("bean", root.getNamespace());
513
	    	
514
	    	for (Element elBean : lsChildren){
515
	    		String strId = elBean.getAttributeValue("id");
516
	    		if (strId != null && strId.endsWith(DATASOURCE_BEAN_POSTFIX)){
517
	    			strId = strId.replace(DATASOURCE_BEAN_POSTFIX, "");
518
	    			dataSources.add(new CdmPersistentDataSource(strId));
519
	    		}
520
	    	}
521
		}
522
		return dataSources;
523
	}
524
	
525
	
526
	/* (non-Javadoc)
527
	 * @see java.lang.Object#toString()
528
	 */
529
	public String toString(){
530
		if (this.dataSourceName != null){
531
			return dataSourceName;
532
		}else{
533
			return null;
534
		}
535
	}
536

    
537

    
538
	
539
	/**
540
	 * Returns the datasource config file input stream.
541
	 * @return data source config file input stream
542
	 */
543
	static protected FileInputStream getDataSourceInputStream(){
544
		String dir = getResourceDirectory();
545
		File file = new File(dir + File.separator +  DATASOURCE_FILE_NAME);
546
		return fileInputStream(file);
547
	}
548
	
549
	
550
	/**
551
	 * Returns the datasource config file outputStream.
552
	 * @return data source config file outputStream
553
	 */
554
	static protected FileOutputStream getDataSourceOutputStream(){
555
		String dir = getResourceDirectory();
556
		File file = new File(dir + File.separator +  DATASOURCE_FILE_NAME);
557
		return fileOutputStream(file);
558
	}
559

    
560
	/**
561
	 * Returns the jdom Element representing the data source bean in the config file.
562
	 * @return
563
	 */
564
	private static Element getDatasourceBeanXml(String strDataSourceName){
565
		FileInputStream inStream = getDataSourceInputStream();
566
		Element root = getBeansRoot(inStream);
567
		if (root == null){
568
			return null;
569
		}else{
570
	    	Element xmlBean = XmlHelp.getFirstAttributedChild(root, "bean", "id", getBeanName(strDataSourceName));
571
			if (xmlBean == null){
572
				//TODO warn or info
573
				logger.debug("Unknown Element 'bean id=" +strDataSourceName + "' ");
574
			};
575
			return xmlBean;
576
		}
577
	}
578
	
579
	// returns the directory containing the resources 
580
	private static String getResourceDirectory(){
581
		File f = CdmApplicationUtils.getWritableResourceDir();
582
		return f.getPath();
583
	}
584
	
585
	static private FileInputStream fileInputStream(File file){
586
		try {
587
			FileInputStream fis = new FileInputStream(file);
588
			return fis;
589
		} catch (FileNotFoundException e) {
590
			logger.warn("File " + file == null?"null":file.getAbsolutePath() + " does not exist in the file system");
591
			return null;
592
		}
593
	}
594
	
595
	static private FileOutputStream fileOutputStream(File file){
596
		try {
597
			FileOutputStream fos = new FileOutputStream(file);
598
			return fos;
599
		} catch (FileNotFoundException e) {
600
			logger.warn("File " + (file == null?"null":file.getAbsolutePath()) + " does not exist in the file system");
601
			return null;
602
		}
603
	}
604
	
605
	
606
	/**
607
	 * Filter class to define datasource file format
608
	 */
609
	private static class DataSourceFileNameFilter implements FilenameFilter{
610
		public boolean accept(File dir, String name) {
611
	        return (name.endsWith(DATASOURCE_FILE_NAME));
612
	    }
613
	}
614
	
615
	public boolean equals(Object obj){
616
		if (obj == null){
617
			return false;
618
		}else if (! CdmPersistentDataSource.class.isAssignableFrom(obj.getClass())){
619
			return false;
620
		}else{
621
			CdmPersistentDataSource dataSource = (CdmPersistentDataSource)obj;
622
			return (this.dataSourceName == dataSource.dataSourceName);
623
		}
624

    
625
	}
626
}
(2-2/11)