2 * Copyright (C) 2007 EDIT
3 * European Distributed Institute of Taxonomy
4 * http://www.e-taxonomy.eu
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.
10 package eu
.etaxonomy
.cdm
.database
;
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
;
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
;
23 import javax
.sql
.DataSource
;
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
;
36 import com
.mchange
.v2
.c3p0
.ComboPooledDataSource
;
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
;
49 * class to access an CdmDataSource
51 public class CdmPersistentDataSource
extends CdmDataSourceBase
implements ICdmPersistentSource
{
52 private static final Logger logger
= Logger
.getLogger(CdmPersistentDataSource
.class);
54 public static final String DATASOURCE_BEAN_POSTFIX
= "DataSource";
57 private String beanName
;
59 private String database
;
62 * This is strictly a <String, String> list of properties
64 private Properties cdmSourceProperties
;
66 private List
<Attribute
> cdmSourceAttributes
;
70 * The Datasource class that Spring will use to set up the connection to the database
72 private static String dataSourceClassName
= ComboPooledDataSource
.class.getName();
76 * Returns the default CdmDataSource
77 * @return the default CdmDataSource
78 * @throws DataSourceNotFoundException
80 public final static CdmPersistentDataSource
NewDefaultInstance() throws DataSourceNotFoundException
{
81 return NewInstance("default");
86 * Returns the default CdmDataSource
87 * @return the default CdmDataSource
88 * @throws DataSourceNotFoundException
90 public final static CdmPersistentDataSource
NewLocalHsqlInstance() throws DataSourceNotFoundException
{
91 return NewInstance("localDefaultHsql");
95 * Returns the CdmDataSource named by strDataSource
96 * @param strDataSource
99 public final static CdmPersistentDataSource
NewInstance(String dataSourceName
) throws DataSourceNotFoundException
{
100 if (exists(dataSourceName
)){
101 return new CdmPersistentDataSource(dataSourceName
);
103 throw new DataSourceNotFoundException("Datasource not found: " + dataSourceName
);
108 * Private Constructor. Use NewXXX factory methods for creating a new instance of CdmDataSource!
109 * @param strDataSource
111 private CdmPersistentDataSource(String strDataSource
){
112 setName(strDataSource
);
113 loadSource(strDataSource
);
116 private void loadSource(String strDataSource
) {
117 CdmPersistentXMLSource cdmPersistentXMLSource
= CdmPersistentXMLSource
.NewInstance(strDataSource
, DATASOURCE_BEAN_POSTFIX
);
118 if(cdmPersistentXMLSource
.getElement() != null) {
119 beanName
= cdmPersistentXMLSource
.getBeanName();
120 // properties from the persistent xml file
121 cdmSourceProperties
= cdmPersistentXMLSource
.getCdmSourceProperties();
122 cdmSourceAttributes
= cdmPersistentXMLSource
.getCdmSourceAttributes();
124 // added database specific properties if they are null
125 String url
= getCdmSourceProperty(CdmSourceProperties
.URL
);
126 DatabaseTypeEnum dbTypeEnum
= getDatabaseType();
127 if (dbTypeEnum
!= null && url
!= null){
128 IDatabaseType dbType
= dbTypeEnum
.getDatabaseType();
129 if (getCdmSourceProperty(CdmSourceProperties
.DATABASE
) == null){
130 String database
= dbType
.getDatabaseNameByConnectionString(url
);
131 if(database
!= null) {
132 setDatabase(database
);
135 if(getCdmSourceProperty(CdmSourceProperties
.SERVER
) == null){
136 String server
= dbType
.getServerNameByConnectionString(url
);
141 if(getCdmSourceProperty(CdmSourceProperties
.PORT
) == null){
142 int port
= dbType
.getPortByConnectionString(url
);
153 public String
getBeanName() {
158 public String
getDatabase() {
164 public void setDatabase(String database
) {
165 this.database
= database
;
167 cdmSourceProperties
.put(CdmSourceProperties
.URL
.toString(), getDatabaseType().getConnectionString(this));
172 public void setServer(String server
) {
173 super.setServer(server
);
175 cdmSourceProperties
.put(CdmSourceProperties
.URL
.toString(), getDatabaseType().getConnectionString(this));
179 public void setPort(int port
) {
181 if(port
!= NULL_PORT
) {
183 cdmSourceProperties
.put(CdmSourceProperties
.URL
.toString(), getDatabaseType().getConnectionString(this));
187 public String
getFilePath() {
188 return getCdmSourceProperty(CdmSourceProperties
.FILEPATH
);
193 public H2Mode
getMode() {
194 return H2Mode
.fromString(getCdmSourceProperty(CdmSourceProperties
.MODE
));
198 public void setMode(H2Mode h2Mode
) {
199 cdmSourceProperties
.put(CdmSourceProperties
.MODE
.toString(), h2Mode
.name());
204 public String
getUsername(){
205 return getCdmSourceProperty(CdmSourceProperties
.USERNAME
);
209 public void setUsername(String username
) {
210 cdmSourceProperties
.put(CdmSourceProperties
.USERNAME
.toString(), username
);
215 public String
getPassword(){
216 return getCdmSourceProperty(CdmSourceProperties
.PASSWORD
);
220 public void setPassword(String password
) {
221 cdmSourceProperties
.put(CdmSourceProperties
.PASSWORD
.toString(), password
);
226 public NomenclaturalCode
getNomenclaturalCode() {
228 return NomenclaturalCode
.fromString(getCdmSourceProperty(CdmSourceProperties
.NOMENCLATURAL_CODE
));
232 public void setNomenclaturalCode(NomenclaturalCode nomenclaturalCode
) {
233 cdmSourceProperties
.put(CdmSourceProperties
.NOMENCLATURAL_CODE
.toString(), nomenclaturalCode
.name());
239 public DatabaseTypeEnum
getDatabaseType(){
240 String strDriverClass
= getCdmSourceProperty(CdmSourceProperties
.DRIVER_CLASS
);
241 DatabaseTypeEnum dbType
= DatabaseTypeEnum
.getDatabaseEnumByDriverClass(strDriverClass
);
246 public String
getCdmSourceProperty(CdmSourceProperties property
){
247 return cdmSourceProperties
.getProperty(property
.toString(),null);
251 * Returns a BeanDefinition object of type DataSource that contains
252 * datsource properties (url, username, password, ...)
255 @SuppressWarnings("unchecked")
257 public BeanDefinition
getDatasourceBean(){
258 DatabaseTypeEnum dbtype
=
259 DatabaseTypeEnum
.getDatabaseEnumByDriverClass(getCdmSourceProperty(CdmSourceProperties
.DRIVER_CLASS
));
261 AbstractBeanDefinition bd
= new RootBeanDefinition(dbtype
.getDataSourceClass());
263 Iterator
<Attribute
> iterator
= cdmSourceAttributes
.iterator();
264 while(iterator
.hasNext()){
265 Attribute attribute
= iterator
.next();
266 if (attribute
.getName().equals("lazy-init")){
267 bd
.setLazyInit(Boolean
.valueOf(attribute
.getValue()));
269 if (attribute
.getName().equals("init-method")){
270 bd
.setInitMethodName(attribute
.getValue());
272 if (attribute
.getName().equals("destroy-method")){
273 bd
.setDestroyMethodName(attribute
.getValue());
275 //Attribute attribute = iterator.next();
276 //bd.setAttribute(attribute.getName(), attribute.getValue());
280 MutablePropertyValues props
= new MutablePropertyValues();
282 Enumeration
<String
> keys
= (Enumeration
)cdmSourceProperties
.keys();
283 while (keys
.hasMoreElements()){
284 String key
= (String
)keys
.nextElement();
286 if (key
.equals("nomenclaturalCode") && cdmSourceProperties
.getProperty(key
).equals("ICBN")){
287 //bugfix for old nomenclatural codes, remove if fixed elsewhere, see https://dev.e-taxonomy.eu/trac/ticket/3658
288 props
.addPropertyValue(key
, NomenclaturalCode
.ICNAFP
.name());
290 props
.addPropertyValue(key
, cdmSourceProperties
.getProperty(key
));
296 bd
.setPropertyValues(props
);
306 public BeanDefinition
getHibernatePropertiesBean(DbSchemaValidation hbm2dll
){
307 boolean showSql
= false;
308 boolean formatSql
= false;
309 boolean registerSearchListener
= false;
310 Class
<?
extends RegionFactory
> cacheProviderClass
= NoCachingRegionFactory
.class;
311 return getHibernatePropertiesBean(hbm2dll
, showSql
, formatSql
, registerSearchListener
, cacheProviderClass
);
321 public BeanDefinition
getHibernatePropertiesBean(DbSchemaValidation hbm2dll
, Boolean showSql
, Boolean formatSql
, Boolean registerSearchListener
, Class
<?
extends RegionFactory
> cacheProviderClass
){
322 //Hibernate default values
323 if (hbm2dll
== null){
324 hbm2dll
= DbSchemaValidation
.VALIDATE
;
326 if (showSql
== null){
329 if (formatSql
== null){
332 if (cacheProviderClass
== null){
333 cacheProviderClass
= NoCachingRegionFactory
.class;
335 if(registerSearchListener
== null){
336 registerSearchListener
= false;
339 DatabaseTypeEnum dbtype
= getDatabaseType();
340 AbstractBeanDefinition bd
= new RootBeanDefinition(PropertiesFactoryBean
.class);
341 MutablePropertyValues hibernateProps
= new MutablePropertyValues();
343 Properties props
= new Properties();
344 props
.setProperty("hibernate.hbm2ddl.auto", hbm2dll
.toString());
345 props
.setProperty("hibernate.dialect", dbtype
.getHibernateDialectCanonicalName());
346 props
.setProperty("hibernate.cache.region.factory_class", cacheProviderClass
.getName());
347 props
.setProperty("hibernate.show_sql", String
.valueOf(showSql
));
348 props
.setProperty("hibernate.format_sql", String
.valueOf(formatSql
));
349 props
.setProperty("hibernate.search.autoregister_listeners", String
.valueOf(registerSearchListener
));
351 hibernateProps
.addPropertyValue("properties",props
);
352 bd
.setPropertyValues(hibernateProps
);
358 * Tests existing of the datsource in the according config file.
359 * @return true if a datasource with the given name exists in the according datasource config file.
361 public static boolean exists(String strDataSourceName
){
362 Element bean
= CdmPersistentSourceUtils
.getCdmSourceBeanXml(strDataSourceName
, DATASOURCE_BEAN_POSTFIX
);
363 return (bean
!= null);
367 * @param strDataSourceName
371 * the updated dataSource, null if not succesful
373 public static CdmPersistentDataSource
update(String strDataSourceName
,
374 ICdmDataSource dataSource
) throws DataSourceNotFoundException
, IllegalArgumentException
{
375 CdmPersistentSourceUtils
.delete(CdmPersistentSourceUtils
.getBeanName(strDataSourceName
,DATASOURCE_BEAN_POSTFIX
));
376 return save(strDataSourceName
, dataSource
);
380 * Replace the persisted datasource with another one.
381 * Used primarily for renaming a datasource.
383 * @param strDataSourceName
386 * @throws DataSourceNotFoundException
387 * @throws IllegalArgumentException
389 public static CdmPersistentDataSource
replace(String strDataSourceName
,
390 ICdmDataSource dataSource
) throws DataSourceNotFoundException
, IllegalArgumentException
{
391 CdmPersistentSourceUtils
.delete(CdmPersistentSourceUtils
.getBeanName(strDataSourceName
,DATASOURCE_BEAN_POSTFIX
));
392 return save(dataSource
);
398 * @throws IllegalArgumentException
400 public static CdmPersistentDataSource
save(ICdmDataSource dataSource
) throws IllegalArgumentException
{
401 return save(dataSource
.getName(),dataSource
);
406 * @param strDataSourceName
407 * @param databaseTypeEnum
413 * @param dataSourceClass
415 * @param destroyMethod
422 private static CdmPersistentDataSource
save(String strDataSourceName
,
423 DatabaseTypeEnum databaseTypeEnum
,
429 Class
<?
extends DataSource
> dataSourceClass
,
431 String destroyMethod
,
436 NomenclaturalCode code
439 int portNumber
= "".equals(port
) ? databaseTypeEnum
.getDefaultPort() : Integer
.valueOf(port
);
441 ICdmDataSource dataSource
= new CdmDataSource(databaseTypeEnum
, server
, database
, portNumber
, username
, password
, filePath
, mode
, code
);
444 Element root
= getBeansRoot(CdmPersistentSourceUtils
.getCdmSourceInputStream());
449 Element bean
= XmlHelp
.getFirstAttributedChild(root
, "bean", "id", CdmPersistentSourceUtils
.getBeanName(strDataSourceName
, DATASOURCE_BEAN_POSTFIX
));
451 bean
.detach(); //delete old version if necessary
453 bean
= insertXmlBean(root
, CdmPersistentSourceUtils
.getBeanName(strDataSourceName
, DATASOURCE_BEAN_POSTFIX
), dataSourceClass
.getName());
455 bean
.setAttribute("lazy-init", "true");
456 if (initMethod
!= null) {bean
.setAttribute("init-method", initMethod
);}
457 if (destroyMethod
!= null) {bean
.setAttribute("destroy-method", destroyMethod
);}
460 insertXmlValueProperty(bean
, "driverClassName", databaseTypeEnum
.getDriverClassName());
462 insertXmlValueProperty(bean
, "url", databaseTypeEnum
.getConnectionString(dataSource
));
463 if (username
!= null) {insertXmlValueProperty(bean
, "username", username
);}
464 if (password
!= null) {insertXmlValueProperty(bean
, "password", password
);}
465 if (startSilent
!= null) {insertXmlValueProperty(bean
, "startSilent", startSilent
.toString() );}
466 if (startServer
!= null) {insertXmlValueProperty(bean
, "startServer", startServer
.toString() );}
467 if (filePath
!= null) {insertXmlValueProperty(bean
, "filePath", filePath
);}
468 if (mode
!= null) {insertXmlValueProperty(bean
, "mode", mode
.toString() );}
469 if (code
!= null) {insertXmlValueProperty(bean
, "nomenclaturalCode", code
.name());}
472 saveToXml(root
.getDocument(),
473 CdmPersistentSourceUtils
.getResourceDirectory(),
474 CdmPersistentXMLSource
.CDMSOURCE_FILE_NAME
,
475 XmlHelp
.prettyFormat
);
477 return NewInstance(strDataSourceName
) ;
478 } catch (DataSourceNotFoundException e
) {
479 logger
.error("Error when saving datasource");
486 * Saves a datasource to the datasource config file. If strDataSourceName differs a new dataSource
487 * will be created in config file. Use update() of real update functionality.
489 * @param strDataSourceName
493 public static CdmPersistentDataSource
save(String strDataSourceName
,
494 ICdmDataSource dataSource
) throws IllegalArgumentException
{
496 if(dataSource
.getDatabaseType() == null){
497 new IllegalArgumentException("Database type not specified");
500 if(dataSource
.getDatabaseType().equals(DatabaseTypeEnum
.H2
)){
501 Class
<?
extends DataSource
> dataSourceClass
= LocalH2
.class;
502 if(dataSource
.getMode() == null) {
503 new IllegalArgumentException("H2 mode not specified");
507 dataSource
.getDatabaseType(),
509 getCheckedDataSourceParameter(dataSource
.getDatabase()),
510 dataSource
.getDatabaseType().getDefaultPort() + "",
511 getCheckedDataSourceParameter(dataSource
.getUsername()),
512 getCheckedDataSourceParameter(dataSource
.getPassword()),
514 null, null, null, null,
515 dataSource
.getFilePath(),
516 dataSource
.getMode(),
517 dataSource
.getNomenclaturalCode());
520 Class
<?
extends DataSource
> dataSourceClass
;
522 dataSourceClass
= (Class
<?
extends DataSource
>) Class
.forName(dataSourceClassName
);
523 String server
= getCheckedDataSourceParameter(dataSource
.getServer());
524 CdmPersistentDataSource persistendDatasource
= save(
526 dataSource
.getDatabaseType(),
527 getCheckedDataSourceParameter(dataSource
.getServer()),
528 getCheckedDataSourceParameter(dataSource
.getDatabase()),
529 dataSource
.getPort() + "",
530 getCheckedDataSourceParameter(dataSource
.getUsername()),
531 getCheckedDataSourceParameter(dataSource
.getPassword()),
533 null, null, null, null, null, null,
534 dataSource
.getNomenclaturalCode());
536 return persistendDatasource
;
537 } catch (ClassNotFoundException e
) {
538 logger
.error("DataSourceClass not found - stopping application", e
);
541 // will never be reached
546 private static String
getCheckedDataSourceParameter(String parameter
) throws IllegalArgumentException
{
547 if(parameter
!= null) {
550 throw new IllegalArgumentException("Non obsolete paramater was assigned a null value: " + parameter
);
556 * Returns a list of all datasources stored in the datasource config file
557 * @return all existing data sources
559 @SuppressWarnings("unchecked")
560 static public List
<CdmPersistentDataSource
> getAllDataSources(){
561 List
<CdmPersistentDataSource
> dataSources
= new ArrayList
<CdmPersistentDataSource
>();
563 Element root
= getBeansRoot(CdmPersistentSourceUtils
.getCdmSourceInputStream());
567 List
<Element
> lsChildren
= root
.getChildren("bean", root
.getNamespace());
569 for (Element elBean
: lsChildren
){
570 String strId
= elBean
.getAttributeValue("id");
571 if (strId
!= null && strId
.endsWith(DATASOURCE_BEAN_POSTFIX
)){
572 strId
= strId
.replace(DATASOURCE_BEAN_POSTFIX
, "");
573 dataSources
.add(new CdmPersistentDataSource(strId
));
582 public boolean equals(Object obj
){
585 }else if (! CdmPersistentDataSource
.class.isAssignableFrom(obj
.getClass())){
588 CdmPersistentDataSource dataSource
= (CdmPersistentDataSource
)obj
;
589 return (getName() == dataSource
.getName());
595 public String
toString(){
596 if (getName() != null){