(no commit message)
[cdmlib.git] / cdmlib-persistence / src / main / java / eu / etaxonomy / cdm / database / CdmDataSource.java
1 package eu.etaxonomy.cdm.database;
2
3 import java.io.File;
4 import java.io.FileInputStream;
5 import java.io.FileNotFoundException;
6 import java.io.FileOutputStream;
7 import java.io.FilenameFilter;
8 import java.util.ArrayList;
9 import java.util.Enumeration;
10 import java.util.HashMap;
11 import java.util.Iterator;
12 import java.util.List;
13 import java.util.Map;
14 import java.util.Properties;
15
16 import org.apache.log4j.Logger;
17 import org.hibernate.cache.CacheProvider;
18 import org.hibernate.cache.NoCacheProvider;
19 import org.jdom.Attribute;
20 import org.jdom.Document;
21 import org.jdom.Element;
22 import org.jdom.output.Format;
23 import org.springframework.beans.MutablePropertyValues;
24 import org.springframework.beans.factory.config.BeanDefinition;
25 import org.springframework.beans.factory.config.PropertiesFactoryBean;
26 import org.springframework.beans.factory.support.AbstractBeanDefinition;
27 import org.springframework.beans.factory.support.RootBeanDefinition;
28 import org.springframework.jdbc.datasource.DriverManagerDataSource;
29
30 import eu.etaxonomy.cdm.api.application.CdmApplicationUtils;
31 import eu.etaxonomy.cdm.common.CdmUtils;
32 import eu.etaxonomy.cdm.common.XmlHelp;
33
34 import static eu.etaxonomy.cdm.common.XmlHelp.getFirstAttributedChild;
35 import static eu.etaxonomy.cdm.common.XmlHelp.getOrAddChild;
36 import static eu.etaxonomy.cdm.common.XmlHelp.getRoot;
37 import static eu.etaxonomy.cdm.common.XmlHelp.insertXmlBean;
38 import static eu.etaxonomy.cdm.common.XmlHelp.insertXmlRefProperty;
39 import static eu.etaxonomy.cdm.common.XmlHelp.insertXmlValueProperty;
40 import static eu.etaxonomy.cdm.common.XmlHelp.saveToXml;
41
42
43 /**
44 * class to access an CdmDataSource
45 */
46 public class CdmDataSource {
47 private static final Logger logger = Logger.getLogger(CdmDataSource.class);
48
49 public static final String DATASOURCE_BEAN_POSTFIX = "DataSource";
50 public final static String DATASOURCE_FILE_NAME = "cdm.datasources.xml";
51 private final static Format format = Format.getPrettyFormat();
52
53 public enum DbProperties{
54 DRIVER_CLASS,
55 URL,
56 USERNAME,
57 PASSWORD;
58
59 @Override
60 public String toString(){
61 switch (this){
62 case DRIVER_CLASS:
63 return "driverClassName";
64 case URL:
65 return "url";
66 case USERNAME:
67 return "username";
68 case PASSWORD:
69 return "password";
70 default:
71 throw new IllegalArgumentException( "Unknown enumeration type" );
72 }
73 }
74 }
75
76 public enum HBM2DDL{
77 VALIDATE,
78 UPDATE,
79 CREATE,
80 CREATE_DROP;
81
82 @Override
83 public String toString(){
84 switch (this){
85 case VALIDATE:
86 return "validate";
87 case UPDATE:
88 return "update";
89 case CREATE:
90 return "create";
91 case CREATE_DROP:
92 return "create-drop";
93 default:
94 throw new IllegalArgumentException( "Unknown enumeration type" );
95 }
96 }
97 }
98
99 //name
100 protected String dataSourceName;
101
102
103 /**
104 * Returns the default CdmDataSource
105 * @return the default CdmDataSource
106 */
107 public final static CdmDataSource NewDefaultInstance(){
108 try {
109 return NewInstance("default");
110 } catch (DataSourceNotFoundException e) {
111 logger.error("Default datasource does not exist in config file");
112 return null;
113 }
114 }
115
116
117 /**
118 * Returns the default CdmDataSource
119 * @return the default CdmDataSource
120 */
121 public final static CdmDataSource NewLocalHsqlInstance(){
122 try {
123 return NewInstance("localDefaultHsql");
124 } catch (DataSourceNotFoundException e) {
125 logger.error("Local datasource does not exist in config file");
126 return null;
127 }
128 }
129
130 /**
131 * Returns the CdmDataSource named by strDataSource
132 * @param strDataSource
133 * @return
134 */
135 public final static CdmDataSource NewInstance(String dataSourceName)
136 throws DataSourceNotFoundException{
137 if (exists(dataSourceName)){
138 return new CdmDataSource(dataSourceName);
139 }else{
140 throw new DataSourceNotFoundException("Datasource not found: " + dataSourceName);
141 }
142 }
143
144 /**
145 * Private Constructor. Use NewXXX factory methods for creating a new instance of CdmDataSource!
146 * @param strDataSource
147 */
148 private CdmDataSource(String strDataSource){
149 dataSourceName = strDataSource;
150 }
151
152 /**
153 * Returns the name of the bean.
154 * @return
155 */
156 public String getName(){
157 return dataSourceName;
158 }
159
160
161 /**
162 * Returns the name of the bean Element in the xml config file.
163 * @return bean name
164 */
165 private static String getBeanName(String name){
166 return name == null? null : name + DATASOURCE_BEAN_POSTFIX;
167 }
168
169
170
171 /**
172 * Returns the database type of the data source.
173 * @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.
174 */
175 public DatabaseTypeEnum getDatabaseType(){
176 Element bean = getDatasourceBeanXml(this.dataSourceName);
177 if (bean == null){
178 return null;
179 }else{
180 Element driverProp = XmlHelp.getFirstAttributedChild(bean, "property", "name", "driverClassName");
181 if (driverProp == null){
182 logger.warn("Unknown property driverClass");
183 return null;
184 }else{
185 String strDriverClass = driverProp.getAttributeValue("value");
186 DatabaseTypeEnum dbType = DatabaseTypeEnum.getDatabaseEnumByDriverClass(strDriverClass);
187 return dbType;
188 }
189 }
190 }
191
192
193
194 /**
195 * Returns a defined property of the datasource
196 * @return the property of the data source. NULL if the datasource bean or the property does not exist.
197 */
198 public String getDbProperty(DbProperties dbProp){
199 Element bean = getDatasourceBeanXml(this.dataSourceName);
200 if (bean == null){
201 return null;
202 }else{
203 Element elProperty = XmlHelp.getFirstAttributedChild(bean, "property", "name", dbProp.toString());
204 if (elProperty == null){
205 logger.warn("Unknown property: " + dbProp.toString());
206 return null;
207 }else{
208 String strValue = elProperty.getAttributeValue("value");
209 return strValue;
210 }
211 }
212 }
213
214
215 /**
216 * Returns the list of properties that are defined in the datasource
217 * @return
218 */
219 public Properties getDbProperties(){
220 Properties result = new Properties();
221 Element bean = getDatasourceBeanXml(this.dataSourceName);
222 if (bean == null){
223 return null;
224 }else{
225 List<Element> elProperties = XmlHelp.getAttributedChildList(bean, "property", "name");
226 Iterator<Element> iterator = elProperties.iterator();
227 while(iterator.hasNext()){
228 Element next = iterator.next();
229 String strName = next.getAttributeValue("name");
230 String strValue = next.getAttributeValue("value");
231 result.put(strName, strValue);
232 }
233 }
234 return result;
235 }
236
237 /**
238 * Returns a BeanDefinition object of type DriverManagerDataSource that contains
239 * datsource properties (url, username, password, ...)
240 * @return
241 */
242 public BeanDefinition getDatasourceBean(){
243 DatabaseTypeEnum dbtype = DatabaseTypeEnum.getDatabaseEnumByDriverClass(getDbProperty(DbProperties.DRIVER_CLASS));
244
245 AbstractBeanDefinition bd = new RootBeanDefinition(dbtype.getDriverManagerDataSourceClass());
246
247 MutablePropertyValues props = new MutablePropertyValues();
248 Properties persistentProperties = getDbProperties();
249 Enumeration<String> keys = (Enumeration)persistentProperties.keys();
250 while (keys.hasMoreElements()){
251 String key = (String)keys.nextElement();
252 props.addPropertyValue(key, persistentProperties.getProperty(key));
253 }
254 // props.addPropertyValue("driverClassName", dbtype.getDriverClassName());
255 // props.addPropertyValue("url", getDbProperty(DbProperties.URL));
256 // props.addPropertyValue("username", getDbProperty(DbProperties.USERNAME));
257 // props.addPropertyValue("password", getDbProperty(DbProperties.PASSWORD));
258 bd.setPropertyValues(props);
259 return bd;
260 }
261
262 /**
263 * @param hbm2dll
264 * @param showSql
265 * @return
266 */
267 public BeanDefinition getHibernatePropertiesBean(HBM2DDL hbm2dll){
268 boolean showSql = false;
269 boolean formatSql = false;
270 Class<? extends CacheProvider> cacheProviderClass = NoCacheProvider.class;
271 return getHibernatePropertiesBean(hbm2dll, showSql, formatSql, cacheProviderClass);
272 }
273
274
275 /**
276 * @param hbm2dll
277 * @param showSql
278 * @return
279 */
280 public BeanDefinition getHibernatePropertiesBean(HBM2DDL hbm2dll, Boolean showSql, Boolean formatSql, Class<? extends CacheProvider> cacheProviderClass){
281 //Hibernate default values
282 if (hbm2dll == null){
283 hbm2dll = HBM2DDL.VALIDATE;
284 }
285 if (showSql == null){
286 showSql = false;
287 }
288 if (formatSql == null){
289 formatSql = false;
290 }
291 if (cacheProviderClass == null){
292 cacheProviderClass = NoCacheProvider.class;
293 }
294
295 DatabaseTypeEnum dbtype = getDatabaseType();
296 AbstractBeanDefinition bd = new RootBeanDefinition(PropertiesFactoryBean.class);
297 MutablePropertyValues hibernateProps = new MutablePropertyValues();
298
299 Properties props = new Properties();
300 props.setProperty("hibernate.hbm2ddl.auto", hbm2dll.toString());
301 props.setProperty("hibernate.dialect", dbtype.getHibernateDialect());
302 props.setProperty("hibernate.cache.provider_class", cacheProviderClass.getName());
303 props.setProperty("hibernate.show_sql", String.valueOf(showSql));
304 props.setProperty("hibernate.format_sql", String.valueOf(formatSql));
305
306 hibernateProps.addPropertyValue("properties",props);
307 bd.setPropertyValues(hibernateProps);
308 return bd;
309 }
310
311
312 /**
313 * Tests existing of the datsource in the according config file.
314 * @return true if a datasource with the given name exists in the according datasource config file.
315 */
316 public static boolean exists(String strDataSourceName){
317 Element bean = getDatasourceBeanXml(strDataSourceName);
318 return (bean != null);
319 }
320
321
322 /**
323 * Saves or updates the datasource to the datasource config file.
324 * Uses default port.
325 * @param strDataSourceName name of the datasource (without postfix DataSource)
326 * @param databaseTypeEnum
327 * @param server
328 * @param database
329 * @param username
330 * @param password
331 * @return the CdmDataSource, null if not successful.
332 */
333 public static CdmDataSource save(String strDataSourceName, DatabaseTypeEnum databaseTypeEnum, String server, String database,
334 String username, String password){
335 return save(strDataSourceName, databaseTypeEnum, server, database,
336 databaseTypeEnum.getDefaultPort(), username, password);
337 }
338
339 /**
340 * Saves or updates the datasource to the datasource config file.
341 * @param strDataSourceName name of the datasource (without postfix DataSource)
342 * @param databaseTypeEnum
343 * @param server
344 * @param database
345 * @param port
346 * @param username
347 * @param password
348 * @return the CdmDataSource, null if not successful.
349 */
350 public static CdmDataSource save(String strDataSourceName, DatabaseTypeEnum databaseTypeEnum, String server, String database,
351 int port, String username, String password){
352 Class<? extends DriverManagerDataSource> driverManagerDataSource = DriverManagerDataSource.class;
353 return save(strDataSourceName, databaseTypeEnum, server, database, port, username, password, driverManagerDataSource, null, null, null, null, null);
354 }
355
356
357 public static CdmDataSource saveLocalHsqlDb(String strDataSourceName, String databasePath, String databaseName, String username, String password){
358 DatabaseTypeEnum databaseTypeEnum = DatabaseTypeEnum.HSqlDb;
359 Class<? extends DriverManagerDataSource> driverManagerDataSource = LocalHsqldb.class;
360 String server = "localhost";
361 int port = databaseTypeEnum.getDefaultPort();
362 return save(strDataSourceName, databaseTypeEnum, server, databaseName, port, username, password, driverManagerDataSource, "init", "destroy", true, true, databasePath);
363 }
364
365 //
366 private static CdmDataSource save(String strDataSourceName,
367 DatabaseTypeEnum databaseTypeEnum,
368 String server,
369 String database,
370 int port,
371 String username,
372 String password,
373 Class<? extends DriverManagerDataSource> driverManagerDataSource,
374 String initMethod,
375 String destroyMethod,
376 Boolean startSilent,
377 Boolean startServer,
378 String databasePath
379 ){
380 //root
381 Element root = getRoot(getDataSourceInputStream());
382 if (root == null){
383 return null;
384 }
385 //bean
386 Element bean = XmlHelp.getFirstAttributedChild(root, "bean", "id", getBeanName(strDataSourceName));
387 if (bean != null){
388 bean.detach(); //delete old version if necessary
389 }
390 bean = insertXmlBean(root, getBeanName(strDataSourceName), driverManagerDataSource.getName());
391 //attributes
392 bean.setAttribute("lazy-init", "true");
393 if (initMethod != null) {bean.setAttribute("init-method", initMethod);}
394 if (destroyMethod != null) {bean.setAttribute("destroy-method", destroyMethod);}
395
396 //set properties
397 insertXmlValueProperty(bean, "driverClassName", databaseTypeEnum.getDriverClassName());
398 insertXmlValueProperty(bean, "url", databaseTypeEnum.getConnectionString(server, database, port));
399 if (username != null) {insertXmlValueProperty(bean, "username", username );}
400 if (password != null) {insertXmlValueProperty(bean, "password", password );}
401 if (startSilent != null) {insertXmlValueProperty(bean, "startSilent", startSilent.toString() );}
402 if (startServer != null) {insertXmlValueProperty(bean, "startServer", startServer.toString() );}
403 if (startServer != null) {insertXmlValueProperty(bean, "databasePath", databasePath );}
404
405 //save
406 saveToXml(root.getDocument(), getResourceDirectory(), DATASOURCE_FILE_NAME, format );
407 try {
408 return NewInstance(strDataSourceName) ;
409 } catch (DataSourceNotFoundException e) {
410 logger.error("Error when saving datasource");
411 return null;
412 }
413 }
414
415
416 /**
417 * Deletes a dataSource
418 * @param dataSource
419 */
420 public static void delete (CdmDataSource dataSource){
421 Element bean = getDatasourceBeanXml(dataSource.getName());
422 if (bean != null){
423 Document doc = bean.getDocument();
424 bean.detach();
425 saveToXml(doc, getDataSourceOutputStream(), format );
426 }
427 }
428
429
430 /**
431 * Returns a list of all datasources stored in the datasource config file
432 * @return all existing data sources
433 */
434 static public List<CdmDataSource> getAllDataSources(){
435 List<CdmDataSource> dataSources = new ArrayList<CdmDataSource>();
436
437 Element root = getRoot(getDataSourceInputStream());
438 if (root == null){
439 return null;
440 }else{
441 List<Element> lsChildren = root.getChildren("bean", root.getNamespace());
442
443 for (Element elBean : lsChildren){
444 String strId = elBean.getAttributeValue("id");
445 if (strId != null && strId.endsWith(DATASOURCE_BEAN_POSTFIX)){
446 strId = strId.replace(DATASOURCE_BEAN_POSTFIX, "");
447 dataSources.add(new CdmDataSource(strId));
448 }
449 }
450 }
451 return dataSources;
452 }
453
454
455 /* (non-Javadoc)
456 * @see java.lang.Object#toString()
457 */
458 public String toString(){
459 if (this.dataSourceName != null){
460 return dataSourceName;
461 }else{
462 return null;
463 }
464 }
465
466
467
468 /**
469 * Returns the datasource config file input stream.
470 * @return data source config file input stream
471 */
472 static protected FileInputStream getDataSourceInputStream(){
473 String dir = getResourceDirectory();
474 File file = new File(dir + File.separator + DATASOURCE_FILE_NAME);
475 return fileInputStream(file);
476 }
477
478
479 /**
480 * Returns the datasource config file outputStream.
481 * @return data source config file outputStream
482 */
483 static protected FileOutputStream getDataSourceOutputStream(){
484 String dir = getResourceDirectory();
485 File file = new File(dir + File.separator + DATASOURCE_FILE_NAME);
486 return fileOutputStream(file);
487 }
488
489 /**
490 * Returns the jdom Element representing the data source bean in the config file.
491 * @return
492 */
493 private static Element getDatasourceBeanXml(String strDataSourceName){
494 Element root = getRoot(getDataSourceInputStream());
495 if (root == null){
496 return null;
497 }else{
498 Element xmlBean = XmlHelp.getFirstAttributedChild(root, "bean", "id", getBeanName(strDataSourceName));
499 if (xmlBean == null){
500 //TODO warn or info
501 logger.debug("Unknown Element 'bean id=" +strDataSourceName + "' ");
502 };
503 return xmlBean;
504 }
505 }
506
507 // returns the directory containing the resources
508 private static String getResourceDirectory(){
509 File f = CdmApplicationUtils.getWritableResourceDir();
510 return f.getPath();
511 }
512
513 static private FileInputStream fileInputStream(File file){
514 try {
515 FileInputStream fis = new FileInputStream(file);
516 return fis;
517 } catch (FileNotFoundException e) {
518 logger.warn("File " + file == null?"null":file.getAbsolutePath() + " does not exist in the file system");
519 return null;
520 }
521 }
522
523 static private FileOutputStream fileOutputStream(File file){
524 try {
525 FileOutputStream fos = new FileOutputStream(file);
526 return fos;
527 } catch (FileNotFoundException e) {
528 logger.warn("File " + (file == null?"null":file.getAbsolutePath()) + " does not exist in the file system");
529 return null;
530 }
531 }
532
533
534 /**
535 * Filter class to define datasource file format
536 */
537 private static class DataSourceFileNameFilter implements FilenameFilter{
538 public boolean accept(File dir, String name) {
539 return (name.endsWith(DATASOURCE_FILE_NAME));
540 }
541 }
542
543 public boolean equals(Object obj){
544 if (obj == null){
545 return false;
546 }else if (! CdmDataSource.class.isAssignableFrom(obj.getClass())){
547 return false;
548 }else{
549 CdmDataSource dataSource = (CdmDataSource)obj;
550 return (this.dataSourceName == dataSource.dataSourceName);
551 }
552
553 }
554 }