Project

General

Profile

Download (8.23 KB) Statistics
| Branch: | Tag: | Revision:
1
// $Id$
2
/**
3
 * Copyright (C) 2009 EDIT
4
 * European Distributed Institute of Taxonomy 
5
 * http://www.e-taxonomy.eu
6
 * 
7
 * The contents of this file are subject to the Mozilla Public License Version 1.1
8
 * See LICENSE.TXT at the top of this package for the full license terms.
9
 */
10

    
11
package eu.etaxonomy.cdm.remote.config;
12

    
13
import java.io.File;
14
import java.lang.reflect.InvocationTargetException;
15
import java.lang.reflect.Method;
16
import java.sql.Connection;
17
import java.sql.ResultSet;
18
import java.sql.SQLException;
19
import java.util.Properties;
20

    
21
import javax.naming.NamingException;
22
import javax.sql.DataSource;
23

    
24
import org.apache.log4j.Logger;
25
import org.springframework.beans.factory.xml.XmlBeanFactory;
26
import org.springframework.context.annotation.Bean;
27
import org.springframework.context.annotation.Configuration;
28
import org.springframework.core.io.FileSystemResource;
29
import org.springframework.jndi.JndiObjectFactoryBean;
30

    
31
import com.mchange.v2.c3p0.ComboPooledDataSource;
32

    
33
import eu.etaxonomy.cdm.model.common.CdmMetaData;
34
import eu.etaxonomy.cdm.model.common.CdmMetaData.MetaDataPropertyName;
35

    
36
/**
37
 * The <code>DataSourceConfigurer</code> can be used as a replacement for a xml configuration in the application context.
38
 * Enter the following in your application context configuration in order to enable the <code>DataSourceConfigurer</code>:
39
 *  
40
<pre>
41
&lt;!-- enable processing of annotations such as @Autowired and @Configuration --&gt;
42
&lt;context:annotation-config/&gt;
43
    
44
&lt;bean class="eu.etaxonomy.cdm.remote.config.DataSourceConfigurer" &gt;
45
&lt;/bean&gt;
46
</pre>
47
 * The <code>DataSourceConfigurer</code> allows alternative ways to specify a data source:
48
 * 
49
 * <ol>
50
 * <li>Specify the data source bean to use in the Java environment properties: 
51
 * <code>-Dcdm.datasource={dataSourceName}</code> ({@link #ATTRIBUTE_DATASOURCE_NAME}). 
52
 * The data source bean with the given name will then be loaded from the <code>cdm.beanDefinitionFile</code> 
53
 * ({@link #CDM_BEAN_DEFINITION_FILE}), which must be a valid Spring bean definition file.
54
 * </li>
55
 * <li> 
56
 * Use a JDBC data source which is bound into the JNDI context. In this case the JNDI name is specified 
57
 * via the {@link #ATTRIBUTE_JDBC_JNDI_NAME} as attribute to the ServletContext. 
58
 * This scenario usually being used by the cdm-server application.
59
 * </li>
60
 * </ol>
61
 * The attributes used in (1) and (2) are in a first step being searched in the ServletContext 
62
 * if not found search in a second step in the environment variables of the OS, see:{@link #findProperty(String, boolean)}. 
63
 * 
64
 * @author a.kohlbecker
65
 * @date 04.02.2011
66
 *
67
 */
68
@Configuration
69
public class DataSourceConfigurer extends AbstractWebApplicationConfigurer {
70
	
71
	public static final Logger logger = Logger.getLogger(DataSourceConfigurer.class);
72

    
73
    private static final String ATTRIBUTE_JDBC_JNDI_NAME = "cdm.jdbcJndiName";
74
    private static final String ATTRIBUTE_HIBERNATE_DIALECT = "hibernate.dialect";
75
    private static final String CDM_BEAN_DEFINITION_FILE = "cdm.beanDefinitionFile";
76
    private static final String ATTRIBUTE_DATASOURCE_NAME = "cdm.datasource";
77

    
78
    private static final String DATASOURCE_BEANDEF_DEFAULT = System.getProperty("user.home")+File.separator+".cdmLibrary"+File.separator+"datasources.xml";
79

    
80
	private static String beanDefinitionFile = DATASOURCE_BEANDEF_DEFAULT;
81
	
82
	public void setBeanDefinitionFile(String filename){
83
		beanDefinitionFile = filename;
84
	}
85
	
86
	
87
	private DataSource dataSource;
88
	
89
	private Properties getHibernateProperties() {
90
		Properties hibernateProperties = webApplicationContext.getBean("jndiHibernateProperties", Properties.class);
91
		return hibernateProperties;
92
	}
93

    
94

    
95
	
96
	@Bean
97
	public DataSource dataSource() {
98
		
99
		String beanName = findProperty(ATTRIBUTE_DATASOURCE_NAME, true);
100
		String jndiName = null;
101
		if(this.dataSource == null){
102
			jndiName = findProperty(ATTRIBUTE_JDBC_JNDI_NAME, false);
103
			
104
			if(jndiName != null){
105
				dataSource = useJndiDataSource(jndiName);
106
			} else {
107
				dataSource = loadDataSourceBean(beanName);
108
			}
109
		}
110
		
111
		if(dataSource == null){
112
			return null;
113
		} 
114
		
115
		// validate correct schema version
116
		try {
117
			
118
			Connection connection = dataSource.getConnection();
119

    
120
			ResultSet resultSet = connection.createStatement().executeQuery(MetaDataPropertyName.DB_SCHEMA_VERSION.getSqlQuery());
121
			String version = null;
122
			if(resultSet.next()){
123
				version = resultSet.getString(1);
124
			} else {
125
				throw new RuntimeException("Unable to retrieve version info from data source " + dataSource.toString());
126
			}
127
			
128
			connection.close();
129

    
130
			if(!CdmMetaData.isDbSchemaVersionCompatible(version)){
131
				/*
132
				 * any exception thrown here would be nested into a spring
133
				 * BeanException which can not be caught in the servlet
134
				 * container, so we post the information into the
135
				 * ServletContext
136
				 */
137
				String errorMessage = "Incompatible version [" + (beanName != null ? beanName : jndiName) + "] expected version: " + CdmMetaData.getDbSchemaVersion() + ",  data base version  " + version;
138
				addErrorMessageToServletContextAttributes(errorMessage);
139
			}
140
			
141
			
142
		} catch (SQLException e) {
143
			RuntimeException re =   new RuntimeException("Unable to connect or to retrieve version info from data source " + dataSource.toString() , e);
144
			addErrorMessageToServletContextAttributes(re.getMessage());
145
			throw re;
146
			
147
		}
148
		return dataSource; 
149
	}
150

    
151

    
152
	private DataSource useJndiDataSource(String jndiName) {
153
		logger.info("using jndi datasource '" + jndiName + "'");
154

    
155
		JndiObjectFactoryBean jndiFactory = new JndiObjectFactoryBean();
156
		/*
157
		JndiTemplate jndiTemplate = new JndiTemplate();
158
		jndiFactory.setJndiTemplate(jndiTemplate); no need to use a JndiTemplate 
159
		if I try using JndiTemplate I get an org.hibernate.AnnotationException: "Unknown Id.generator: system-increment" 
160
		when running multiple intances via the Bootloader
161
		*/
162
		jndiFactory.setResourceRef(true);
163
		jndiFactory.setJndiName(jndiName);
164
		try {
165
			jndiFactory.afterPropertiesSet();
166
		} catch (IllegalArgumentException e) {
167
			logger.error(e);
168
		} catch (NamingException e) {
169
			logger.error(e);
170
		}
171
		Object obj = jndiFactory.getObject();
172
		return (DataSource)obj;
173
	}
174
	
175
	private DataSource loadDataSourceBean(String beanName) {
176
		
177
		String beanDefinitionFileFromProperty = findProperty(CDM_BEAN_DEFINITION_FILE, false);
178
		String path = (beanDefinitionFileFromProperty != null ? beanDefinitionFileFromProperty : beanDefinitionFile);
179
		logger.info("loading DataSourceBean '" + beanName + "' from: " + path);
180
		FileSystemResource file = new FileSystemResource(path);
181
		XmlBeanFactory beanFactory  = new XmlBeanFactory(file);
182
		DataSource dataSource = beanFactory.getBean(beanName, DataSource.class);
183
		if(dataSource instanceof ComboPooledDataSource){
184
			logger.info("DataSourceBean '" + beanName + "' is a ComboPooledDataSource [URL:" + ((ComboPooledDataSource)dataSource).getJdbcUrl()+ "]");
185
		} else {
186
			logger.error("DataSourceBean '" + beanName + "' IS NOT a ComboPooledDataSource");
187
		}
188
		return dataSource;
189
	}
190
	
191
	@Bean
192
	public Properties hibernateProperties(){
193
		Properties props = getHibernateProperties();
194
		props.setProperty(ATTRIBUTE_HIBERNATE_DIALECT, inferHibernateDialectName());
195
		return props;
196
	}
197

    
198
	public String inferHibernateDialectName() {
199
		DataSource ds = dataSource();
200
		String url = "<SEE PRIOR REFLECTION ERROR>";
201
		Method m = null;
202
		try {
203
			m = ds.getClass().getMethod("getUrl");
204
		} catch (SecurityException e) {
205
			logger.error(e);
206
		} catch (NoSuchMethodException e) {
207
			try {
208
				m = ds.getClass().getMethod("getJdbcUrl");
209
			} catch (SecurityException e2) {
210
				logger.error(e2);
211
			} catch (NoSuchMethodException e2) {
212
				logger.error(e2);
213
			}
214
		}
215
		try {
216
			url = (String)m.invoke(ds);
217
		} catch (IllegalArgumentException e) {
218
			logger.error(e);
219
		} catch (IllegalAccessException e) {
220
			logger.error(e);
221
		} catch (InvocationTargetException e) {
222
			logger.error(e);
223
		} catch (SecurityException e) {
224
			logger.error(e);
225
		} 
226
		
227
		if(url != null && url.contains("mysql")){
228
			return "org.hibernate.dialect.MySQLDialect";
229
		}
230
		
231
		logger.error("hibernate dialect mapping for "+url+ " not jet implemented or unavailable");
232
		return null;
233
	}
234

    
235
}
(2-2/3)