Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import org.springframework.context.ApplicationContext
import org.springframework.context.ApplicationEventPublisher
import org.springframework.context.support.GenericApplicationContext
import org.springframework.core.env.ConfigurableEnvironment
import org.springframework.core.env.MapPropertySource
import org.springframework.core.env.PropertyResolver
import org.springframework.transaction.PlatformTransactionManager

Expand All @@ -47,16 +48,13 @@ import org.grails.orm.hibernate.support.HibernateDatastoreConnectionSourcesRegis
*/
class HibernateDatastoreSpringInitializer extends AbstractDatastoreInitializer {

public static final String SESSION_FACTORY_BEAN_NAME = 'sessionFactory'
public static final String DEFAULT_DATA_SOURCE_NAME = Settings.SETTING_DATASOURCE
public static final String DATA_SOURCES = Settings.SETTING_DATASOURCES
public static final String TEST_DB_URL = 'jdbc:h2:mem:grailsDb;LOCK_TIMEOUT=10000;DB_CLOSE_DELAY=-1'

String defaultDataSourceBeanName = ConnectionSource.DEFAULT
String defaultSessionFactoryBeanName = SESSION_FACTORY_BEAN_NAME
Set<String> dataSources = [defaultDataSourceBeanName] as Set<String>
boolean enableReload = false
boolean grailsPlugin = false

HibernateDatastoreSpringInitializer(PropertyResolver configuration, Collection<Class> persistentClasses) {
super(configuration, persistentClasses)
Expand Down Expand Up @@ -99,15 +97,15 @@ class HibernateDatastoreSpringInitializer extends AbstractDatastoreInitializer {
}
Map dataSource = (Map) config.getProperty(DEFAULT_DATA_SOURCE_NAME, Map, Collections.emptyMap())
if (dataSource != null && !dataSource.isEmpty()) {
dataSourceNames.add(ConnectionSource.DEFAULT)
dataSourceNames.add(defaultDataSourceBeanName)
}
}
this.dataSources = dataSourceNames
}

@Override
protected Class<AbstractDatastorePersistenceContextInterceptor> getPersistenceInterceptorClass() {
getClass().classLoader.loadClass('org.grails.plugin.hibernate.support.HibernatePersistenceContextInterceptor')
getClass().classLoader.loadClass('org.grails.plugin.hibernate.support.HibernatePersistenceContextInterceptor') as Class<AbstractDatastorePersistenceContextInterceptor>
}

/**
Expand All @@ -121,10 +119,6 @@ class HibernateDatastoreSpringInitializer extends AbstractDatastoreInitializer {
return applicationContext
}

protected String getTestDbUrl() {
TEST_DB_URL
}

@CompileStatic
ApplicationContext configureForDataSource(DataSource dataSource) {
GenericApplicationContext applicationContext = createApplicationContext()
Expand All @@ -134,7 +128,27 @@ class HibernateDatastoreSpringInitializer extends AbstractDatastoreInitializer {
return applicationContext
}

/**
* Applies {@link #enableReload} as an {@code enableReload} fallback on {@link #configuration}
* when it was customized away from its default and the configuration does not already specify
* it explicitly.
*/
protected void applyEnableReloadFallback() {
if (!enableReload || configuration.containsProperty('enableReload')) {
return
}
if (configuration instanceof ConfigurableEnvironment) {
((ConfigurableEnvironment) configuration).propertySources.addFirst(
new MapPropertySource('hibernateDatastoreSpringInitializer.enableReload', [enableReload: true])
)
}
else if (configuration instanceof Map) {
((Map) configuration).put('enableReload', true)
}
}

Closure getBeanDefinitions(BeanDefinitionRegistry beanDefinitionRegistry) {
applyEnableReloadFallback()
ApplicationEventPublisher eventPublisher = super.findEventPublisher(beanDefinitionRegistry)
Closure beanDefinitions = {
def common = getCommonConfiguration(beanDefinitionRegistry, 'hibernate')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,6 @@ class HibernateGrailsPlugin extends Plugin {

def springInitializer = new HibernateDatastoreSpringInitializer((PropertyResolver) config, domainClasses)
springInitializer.enableReload = Environment.isDevelopmentMode()
springInitializer.registerApplicationIfNotPresent = false
springInitializer.grailsPlugin = true
dataSourceNames = springInitializer.dataSources
def beans = springInitializer.getBeanDefinitions((BeanDefinitionRegistry) applicationContext)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,29 @@
package grails.orm.bootstrap

import grails.gorm.annotation.Entity
import org.grails.datastore.mapping.core.DatastoreUtils
import org.grails.orm.hibernate.HibernateDatastore
import org.hibernate.Session
import org.hibernate.SessionFactory
import org.hibernate.dialect.H2Dialect
import org.springframework.beans.factory.support.RootBeanDefinition
import org.springframework.context.ConfigurableApplicationContext
import org.springframework.context.support.GenericApplicationContext
import org.springframework.jdbc.datasource.DriverManagerDataSource
import org.springframework.transaction.PlatformTransactionManager
import spock.lang.AutoCleanup
import spock.lang.Specification

import javax.sql.DataSource

/**
* Created by graemerocher on 29/01/14.
*/
class HibernateDatastoreSpringInitializerSpec extends Specification{

@AutoCleanup
ConfigurableApplicationContext applicationContext

void "Test configure multiple data sources"() {
given:"An initializer instance"
Map config = [
Expand Down Expand Up @@ -90,6 +102,159 @@ class HibernateDatastoreSpringInitializerSpec extends Specification{
}

}

void "Test configureDataSources uses the customized default data source bean name consistently"() {
given: "an initializer with a customized default data source bean name"
def datastoreInitializer = new HibernateDatastoreSpringInitializer([:], Person)
datastoreInitializer.defaultDataSourceBeanName = 'primary'

when: "data sources are configured from a resolver with only the default data source present"
datastoreInitializer.configureDataSources(DatastoreUtils.createPropertyResolver(['dataSource.url': 'jdbc:h2:mem:customDefaultDsName;LOCK_TIMEOUT=10000']))

then: "the default entry is recorded under the custom name, not the literal ConnectionSource.DEFAULT"
datastoreInitializer.dataSources == ['primary'] as Set<String>
}

void "Test applyEnableReloadFallback injects a fallback when customized and not already configured"() {
given:
def datastoreInitializer = new HibernateDatastoreSpringInitializer([:], Person)
datastoreInitializer.enableReload = true

when:
datastoreInitializer.applyEnableReloadFallback()

then:
datastoreInitializer.configuration.getProperty('enableReload', Boolean) == true
}

void "Test applyEnableReloadFallback does not override an already-configured value"() {
given:
def datastoreInitializer = new HibernateDatastoreSpringInitializer(['enableReload': 'false'], Person)
datastoreInitializer.enableReload = true

when:
datastoreInitializer.applyEnableReloadFallback()

then:
datastoreInitializer.configuration.getProperty('enableReload', Boolean) == false
}

void "Test enableReload is honored end-to-end as a fallback on the default connection's settings"() {
given: "an initializer with enableReload customized and no explicit config for it"
def datastoreInitializer = new HibernateDatastoreSpringInitializer(
['dataSource.url': 'jdbc:h2:mem:enableReloadFallback;LOCK_TIMEOUT=10000'], Person)
datastoreInitializer.enableReload = true

when: "the application is configured"
applicationContext = (ConfigurableApplicationContext) datastoreInitializer.configure()
def settings = applicationContext.getBean(HibernateDatastore).connectionSources.defaultConnectionSource.settings

then: "the fallback reaches the datastore's own connection source settings"
settings.enableReload
}

void "Test the Map/Collection<Class> constructor bootstraps GORM"() {
given: "An initializer built from a Collection of persistent classes"
def datastoreInitializer = new HibernateDatastoreSpringInitializer(
['dataSource.url': 'jdbc:h2:mem:collectionCtor;LOCK_TIMEOUT=10000', 'hibernate.hbm2ddl.auto': 'create'],
[Person] as Collection<Class>)

when: "the application is configured"
applicationContext = (ConfigurableApplicationContext) datastoreInitializer.configure()

then: "GORM is bootstrapped with the given entity"
applicationContext.getBean(HibernateDatastore).mappingContext.getPersistentEntity(Person.name) != null
Person.withNewSession { Person.count() == 0 }
}

void "Test the PropertyResolver/Collection<Class> constructor bootstraps GORM"() {
given: "An initializer built from a PropertyResolver and a Collection of persistent classes"
def resolver = DatastoreUtils.createPropertyResolver([
'dataSource.url' : 'jdbc:h2:mem:propertyResolverCollectionCtor;LOCK_TIMEOUT=10000',
'hibernate.hbm2ddl.auto': 'create'
])
def datastoreInitializer = new HibernateDatastoreSpringInitializer(resolver, [Person] as Collection<Class>)

when: "the application is configured"
applicationContext = (ConfigurableApplicationContext) datastoreInitializer.configure()

then: "GORM is bootstrapped with the given entity"
applicationContext.getBean(HibernateDatastore).mappingContext.getPersistentEntity(Person.name) != null
Person.withNewSession { Person.count() == 0 }
}

void "Test the PropertyResolver/Class... constructor bootstraps GORM"() {
given: "An initializer built from a PropertyResolver and an array of persistent classes"
def resolver = DatastoreUtils.createPropertyResolver([
'dataSource.url' : 'jdbc:h2:mem:propertyResolverClassCtor;LOCK_TIMEOUT=10000',
'hibernate.hbm2ddl.auto': 'create'
])
def datastoreInitializer = new HibernateDatastoreSpringInitializer(resolver, Person)

when: "the application is configured"
applicationContext = (ConfigurableApplicationContext) datastoreInitializer.configure()

then: "GORM is bootstrapped with the given entity"
applicationContext.getBean(HibernateDatastore).mappingContext.getPersistentEntity(Person.name) != null
Person.withNewSession { Person.count() == 0 }
}

void "Test the PropertyResolver/String... packages constructor discovers entities via classpath scan"() {
given: "an initializer configured with a package name rather than explicit classes; the scan also picks up Book and Author, which require the 'books'/'moreBooks' datasources"
def resolver = DatastoreUtils.createPropertyResolver([
'dataSource.url' : 'jdbc:h2:mem:propertyResolverPackageCtor;LOCK_TIMEOUT=10000',
'hibernate.hbm2ddl.auto': 'create',
'dataSources.books.url' : 'jdbc:h2:mem:propertyResolverPackageCtorBooks;LOCK_TIMEOUT=10000',
'dataSources.moreBooks.url': 'jdbc:h2:mem:propertyResolverPackageCtorMoreBooks;LOCK_TIMEOUT=10000'
])
def datastoreInitializer = new HibernateDatastoreSpringInitializer(resolver, Person.package.name)

when: "the application is configured"
applicationContext = (ConfigurableApplicationContext) datastoreInitializer.configure()

then: "the Person entity declared in the scanned package was discovered and mapped"
applicationContext.getBean(HibernateDatastore).mappingContext.getPersistentEntity(Person.name) != null
}

void "Test configureForDataSource bootstraps GORM around a pre-existing DataSource"() {
given: "a DataSource created ahead of time"
def dataSource = new DriverManagerDataSource(HibernateDatastoreSpringInitializer.TEST_DB_URL, 'sa', '')
dataSource.driverClassName = 'org.h2.Driver'
def datastoreInitializer = new HibernateDatastoreSpringInitializer(['hibernate.hbm2ddl.auto': 'create'], Person)

when: "the initializer is configured around that DataSource"
applicationContext = (ConfigurableApplicationContext) datastoreInitializer.configureForDataSource(dataSource)

then: "the pre-existing DataSource is registered and reused rather than a new one being built"
applicationContext.getBean(HibernateDatastoreSpringInitializer.DEFAULT_DATA_SOURCE_NAME, DataSource).is(dataSource)
Person.withNewSession { Person.count() == 0 }
}

void "Test the OSIV interceptor is registered when the registry is a web application"() {
given: "a registry that signals it belongs to a web application"
def datastoreInitializer = new HibernateDatastoreSpringInitializer(['dataSource.url': 'jdbc:h2:mem:osivEnabled;LOCK_TIMEOUT=10000'], Person)
def registry = new GenericApplicationContext()
registry.registerBeanDefinition('grailsControllerHelper', new RootBeanDefinition(Object))

when:
datastoreInitializer.configureForBeanDefinitionRegistry(registry)
registry.refresh()
applicationContext = registry

then:
registry.containsBean('openSessionInViewInterceptor')
}

void "Test the OSIV interceptor is not registered for a non-web application registry"() {
given: "An initializer instance configured against a plain, non-web registry"
def datastoreInitializer = new HibernateDatastoreSpringInitializer(['dataSource.url': 'jdbc:h2:mem:osivDisabled;LOCK_TIMEOUT=10000'], Person)

when:
applicationContext = (ConfigurableApplicationContext) datastoreInitializer.configure()

then:
!applicationContext.containsBean('openSessionInViewInterceptor')
}
}
@Entity
class Person {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import org.springframework.context.ApplicationContext
import org.springframework.context.ApplicationEventPublisher
import org.springframework.context.support.GenericApplicationContext
import org.springframework.core.env.ConfigurableEnvironment
import org.springframework.core.env.MapPropertySource
import org.springframework.core.env.PropertyResolver
import org.springframework.transaction.PlatformTransactionManager

Expand All @@ -49,16 +50,13 @@ import org.grails.orm.hibernate.support.HibernateDatastoreConnectionSourcesRegis
*/
class HibernateDatastoreSpringInitializer extends AbstractDatastoreInitializer {

public static final String SESSION_FACTORY_BEAN_NAME = 'sessionFactory'
public static final String DEFAULT_DATA_SOURCE_NAME = Settings.SETTING_DATASOURCE
public static final String DATA_SOURCES = Settings.SETTING_DATASOURCES
public static final String TEST_DB_URL = 'jdbc:h2:mem:grailsDb;LOCK_TIMEOUT=10000;DB_CLOSE_DELAY=-1'

String defaultDataSourceBeanName = ConnectionSource.DEFAULT
String defaultSessionFactoryBeanName = SESSION_FACTORY_BEAN_NAME
Set<String> dataSources = [defaultDataSourceBeanName] as Set<String>
boolean enableReload = false
boolean grailsPlugin = false
Closure beanDefinitions
protected ApplicationContext applicationContext

Expand Down Expand Up @@ -103,7 +101,7 @@ class HibernateDatastoreSpringInitializer extends AbstractDatastoreInitializer {
}
Map dataSource = (Map) config.getProperty(DEFAULT_DATA_SOURCE_NAME, Map, Collections.emptyMap())
if (dataSource != null && !dataSource.isEmpty()) {
dataSourceNames.add(ConnectionSource.DEFAULT)
dataSourceNames.add(defaultDataSourceBeanName)
}
}
this.dataSources = dataSourceNames
Expand Down Expand Up @@ -139,10 +137,6 @@ class HibernateDatastoreSpringInitializer extends AbstractDatastoreInitializer {
}
}

protected String getTestDbUrl() {
TEST_DB_URL
}

@CompileStatic
ApplicationContext configureForDataSource(DataSource dataSource) {
GenericApplicationContext applicationContext = createApplicationContext()
Expand All @@ -152,7 +146,27 @@ class HibernateDatastoreSpringInitializer extends AbstractDatastoreInitializer {
return applicationContext
}

/**
* Applies {@link #enableReload} as an {@code enableReload} fallback on {@link #configuration}
* when it was customized away from its default and the configuration does not already specify
* it explicitly.
*/
protected void applyEnableReloadFallback() {
if (!enableReload || configuration.containsProperty('enableReload')) {
return
}
if (configuration instanceof ConfigurableEnvironment) {
((ConfigurableEnvironment) configuration).propertySources.addFirst(
new MapPropertySource('hibernateDatastoreSpringInitializer.enableReload', [enableReload: true])
)
}
else if (configuration instanceof Map) {
((Map) configuration).put('enableReload', true)
}
}

Closure getBeanDefinitions(BeanDefinitionRegistry beanDefinitionRegistry) {
applyEnableReloadFallback()
ApplicationEventPublisher eventPublisher = super.findEventPublisher(beanDefinitionRegistry)
return { ->
def common = getCommonConfiguration(beanDefinitionRegistry, 'hibernate')
Expand Down Expand Up @@ -188,7 +202,7 @@ class HibernateDatastoreSpringInitializer extends AbstractDatastoreInitializer {
getBeanDefinition('transactionManager').beanClass = PlatformTransactionManager

for (String dataSourceName in dataSources) {
if (dataSourceName == ConnectionSource.DEFAULT) continue
if (dataSourceName == defaultDataSourceBeanName) continue

"dataSource_$dataSourceName"(hibernateDatastore: 'getDataSource', dataSourceName)
"sessionFactory_$dataSourceName"(hibernateDatastore: 'getSessionFactory', dataSourceName)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,6 @@ class HibernateGrailsPlugin extends Plugin {

def springInitializer = new HibernateDatastoreSpringInitializer((PropertyResolver) config, domainClasses)
springInitializer.enableReload = Environment.isDevelopmentMode()
springInitializer.registerApplicationIfNotPresent = false
springInitializer.grailsPlugin = true
dataSourceNames = springInitializer.dataSources
def beans = springInitializer.getBeanDefinitions((BeanDefinitionRegistry) applicationContext)

Expand Down
Loading
Loading