Eclipse RCP+Spring+EMF+Teneo and the data source
One issue when using these technologies together is the configuration of the HbSessionDataStore. Under spring, you can configure the LocalSessionFactoryBean by setting the data source directly. The data source is typically defined in your context XML as well. But there is no data source property setter under HbSessionDataStore. Under standard hibernate you can set configuration parameter hibernate.connection.provider_class to the name of the connection provider class and spring provides a data source called LocalDataSourceConnectionProvider which can be instantiated without parameters to get a data source. The trick of this spring class is that it access a thread local storage (therefore global location for that thread) to retrieve the data source you may have set in the LocalSessionFactoryBean instance under setDataSource(). The LocalSessionFactoryBean sets the thread local storage when you call setDataSource().
So under teneo’s HbSessionDataStore, we can just extend the class to include this type of property setter, create a small connection provider class just like LocalDataSourceConnectionProvider and do what spring does. Then we just need to ensure that our connection provider is used and that the user does not set it twice, once through the setter and through standard hibernate configuration approaches in the properties specification. Note that the teneo page http://www.elver.org/hibernate/hbdatastore.html describes having to set the data store factory in the HbHelper class. However, since we are using spring, you don’t need to do that (the HbHelper class is acting as a service locator) because it is done for you.
Here’s the example data source connection provider:
1: public class DataSourceConnectionProvider implements ConnectionProvider {
2:
3: private DataSource dataSource;
4: private DataSource dataSourceToUse;
5:
6: @Override
7: public void close() throws HibernateException {
8: }
9:
10: @Override
11: public void closeConnection(Connection arg0) throws SQLException {
12: try{
13: arg0.close();
14: } catch(SQLException ex) {
15: JDBCExceptionReporter.logExceptions(ex);
16: throw ex;
17: }
18:
19: }
20:
21: @Override
22: public void configure(Properties arg0) throws HibernateException {
23: this.dataSource = LocalHbSessionDataStoreBean
24: .getConfigTimeDataSource();
25: if (this.dataSource == null) {
26: throw new HibernateException(
27: "No data source to use. Set data source property on LocalHbSessionDataStore");
28: }
29: this.dataSourceToUse = getDataSourceToUse(this.dataSource);
30: }
31:
32: /**
33: * Return a data source to use. The data source passed in is the one
34: * configured locally. Subclasses may wish to proxy or otherwise modify the
35: * data source in some way.
36: *
37: * @param originalDataSource
38: * @return
39: */
40: protected DataSource getDataSourceToUse(DataSource originalDataSource) {
41: return originalDataSource;
42: }
43:
44: public DataSource getDataSource() {
45: return dataSource;
46: }
47:
48:
49: @Override
50: public Connection getConnection() throws SQLException {
51: try {
52: return this.dataSourceToUse.getConnection();
53: } catch(SQLException ex) {
54: JDBCExceptionReporter.logExceptions(ex);
55: throw ex;
56: }
57: }
58:
59: @Override
60: public boolean supportsAggressiveRelease() {
61: return false;
62: }
63:
64: }
The new bean to use:
1: public class LocalHbSessionDataStoreBean extends HbSessionDataStore {
2:
3: private static final long serialVersionUID = 767809917573382770L;
4:
5: private static final ThreadLocal<DataSource> configTimeDataSourceHolder = new ThreadLocal<DataSource>();
6:
7: public static void setConfigTimeDataSource(DataSource ds) {
8: configTimeDataSourceHolder.set(ds);
9: }
10:
11: public static DataSource getConfigTimeDataSource() {
12: return configTimeDataSourceHolder.get();
13: }
14:
15: public void setDataSource(DataSource ds) {
16: setConfigTimeDataSource(ds);
17: }
18:
19: public DataSource getDataSource() {
20: return getConfigTimeDataSource();
21: }
22:
23: @Override
24: protected void setPropertiesInConfiguration() {
25: super.setPropertiesInConfiguration();
26: getConfiguration().setProperty(
27: org.hibernate.cfg.Environment.CONNECTION_PROVIDER,
28: DataSourceConnectionProvider.class.getName());
29: }
30:
31: }
The namespace org.eclipse.mf.teneo.spring.hibernate has been used but you can change this.
Further investigation is needed to see if the standard spring transaction machinery is working as expected. More on this later.
If you need to dynamically set the properties of the data source, i have blogged about ways to dynamically created hierarchical contexts and other tricks, however, you can easily configure the data source by defining a FactoryBeanPostProcessor, obtaining the bean definition, setting the properties based on your “stored” data source configuration then add the post processor programmatically or by defining the post processor bean in your context (in which case it is automatically picked up). Bean definitions are partially instantiated beans that you can alter/override the configuration of properties with. Its very convenient and easier than what I described in previous blogs. In your post processor callback:
1: public void postProcessBeanFactory(ConfigurableListableBeanFactory arg0)
2: throws BeansException {
3: BeanDefinition bd = arg0.getBeanDefinition("yourDataSourceBeanName");
4: MutablePropertyValues v = bd.getPropertyValues();
5: v.addPropertyValue("username", "yourUsername);
6: v.addPropertyValue("password", "yourPassword");
7: v.addPropertyValue("url", "jdcb:yourURL");
8: v.addPropertyValue("driverClassName", "com.yourSQLDriver");
9: }
Comments
Post a Comment