Skip to content

Instantly share code, notes, and snippets.

@lhotari
Created September 28, 2012 19:59
Show Gist options
  • Save lhotari/3801817 to your computer and use it in GitHub Desktop.
Save lhotari/3801817 to your computer and use it in GitHub Desktop.
GRAILS-9411 and GRAILS-9412 monkey patch for Grails 2.1.1
// src/groovy/grailspatch/PatchedHibernatePluginSupport.groovy
/*
* Copyright 2004-2005 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package grailspatch
import org.apache.commons.logging.Log
import org.apache.commons.logging.LogFactory
import org.codehaus.groovy.grails.commons.GrailsDomainClass
import org.codehaus.groovy.grails.commons.GrailsDomainClassProperty
import org.codehaus.groovy.grails.orm.hibernate.*
import org.codehaus.groovy.grails.orm.hibernate.cfg.GrailsDomainBinder
import org.codehaus.groovy.grails.orm.hibernate.cfg.GrailsHibernateUtil
import org.codehaus.groovy.grails.orm.hibernate.cfg.Mapping
import org.codehaus.groovy.grails.orm.hibernate.support.*
import org.codehaus.groovy.grails.orm.hibernate.validation.HibernateDomainClassValidator
import org.codehaus.groovy.grails.plugins.orm.hibernate.HibernatePluginSupport
import org.grails.datastore.mapping.model.MappingContext
import org.springframework.context.ApplicationContext
import org.springframework.validation.Validator
class PatchedHibernatePluginSupport {
static final Log LOG = LogFactory.getLog(this)
static final int RELOAD_RETRY_LIMIT = 3
public static void clearMappingCache() {
GrailsDomainBinder.MAPPING_CACHE.clear();
}
public static void clearMappingCache(Class<?> theClass) {
String className = theClass.getName();
for(Iterator<Map.Entry<Class<?>, Mapping>> it = GrailsDomainBinder.MAPPING_CACHE.entrySet().iterator(); it.hasNext();) {
Map.Entry<Class<?>, Mapping> entry = it.next();
if(className.equals(entry.getKey().getName())) {
it.remove();
}
}
}
static final onChange = { event ->
LOG.debug "PATCHED onChange() started"
def allDatasourceNames = [GrailsDomainClassProperty.DEFAULT_DATA_SOURCE] as Set
for (name in application.config.keySet()) {
if (name.startsWith('dataSource_')) {
allDatasourceNames << name - 'dataSource_'
}
}
def datasourceNames
if(event.source instanceof Class) {
GrailsDomainClass dc = application.getDomainClass(event.source.name)
if(!dc.getMappingStrategy().equalsIgnoreCase(GrailsDomainClass.GORM)) {
return
}
clearMappingCache(event.source)
def dcMappingDsNames = GrailsHibernateUtil.getDatasourceNames(dc) as Set
datasourceNames = [] as Set
for(name in allDatasourceNames) {
if(name in dcMappingDsNames || dcMappingDsNames.contains(GrailsDomainClassProperty.ALL_DATA_SOURCES)) {
datasourceNames << name
}
}
} else {
clearMappingCache()
datasourceNames = allDatasourceNames
}
def beans = beans {
for (String datasourceName in datasourceNames) {
LOG.debug "processing DataSource $datasourceName"
boolean isDefault = datasourceName == GrailsDomainClassProperty.DEFAULT_DATA_SOURCE
String suffix = isDefault ? '' : '_' + datasourceName
def hibConfig = application.config["hibernate$suffix"]
def sessionFactoryReload = hibConfig?.containsKey('reload') ? hibConfig.reload : true
if(sessionFactoryReload) {
"${SessionFactoryHolder.BEAN_ID}$suffix"(SessionFactoryHolder) {
sessionFactory = bean(ConfigurableLocalSessionFactoryBean) { bean ->
bean.parent = ref("abstractSessionFactoryBeanConfig$suffix")
proxyIfReloadEnabled = false
}
}
if(event.source instanceof Class) {
GrailsDomainClass dc = application.getDomainClass(event.source.name)
if (!dc.abstract && GrailsHibernateUtil.usesDatasource(dc, datasourceName)) {
"${dc.fullName}Validator$suffix"(HibernateDomainClassValidator) {
messageSource = ref("messageSource")
domainClass = ref("${dc.fullName}DomainClass")
sessionFactory = ref("sessionFactory$suffix")
grailsApplication = ref("grailsApplication", true)
}
}
}
}
}
}
ApplicationContext ctx = event.ctx
beans.registerBeans(ctx)
if (event.source instanceof Class) {
def mappingContext = ctx.getBean("grailsDomainClassMappingContext", MappingContext)
def entity = mappingContext.addPersistentEntity(event.source)
def dc = application.getDomainClass(event.source.name)
for (String datasourceName in datasourceNames) {
if (GrailsHibernateUtil.usesDatasource(dc, datasourceName)) {
boolean isDefault = datasourceName == GrailsDomainClassProperty.DEFAULT_DATA_SOURCE
String suffix = isDefault ? '' : '_' + datasourceName
final validator = ctx.getBean("${entity.name}Validator$suffix", Validator)
mappingContext.addEntityValidator(entity, validator)
if (isDefault) {
GrailsDomainClass domainClass = application.getDomainClass(event.source.name)
domainClass.setValidator(validator)
}
}
}
}
int retryCount = 0
def enhanceAndTest
enhanceAndTest = {
// Re-enhance the given class
HibernatePluginSupport.enhanceSessionFactories(ctx, application, event.source)
// Due to quantum tunneling and other class loader race conditions, attempts to
// enhance the entities may not work. Check a few static and non-static methods to see if it worked.
boolean hasMethods = event.source.metaClass.methods.any { MetaMethod method ->
method.name.startsWith("addTo") &&
method.name.startsWith("list") &&
method.name.startsWith("get") &&
method.name.startsWith("count")
}
if( !hasMethods ) {
if( ++retryCount < RELOAD_RETRY_LIMIT ) {
LOG.debug("Attempt ${retryCount} at enhancing ${event.source.name} failed, waiting and trying again")
sleep(retryCount * 1000)
enhanceAndTest()
}
}
}
// Enhance the reloaded GORM objects
enhanceAndTest()
LOG.info "PATCHED onChange() complete"
}
}
// src/groovy/grailspatch/PatchedHibernatePluginSupport.groovy
import grailspatch.PatchedHibernatePluginSupport
import org.codehaus.groovy.grails.web.servlet.GrailsApplicationAttributes
class PatchedHibernatePluginSupportBootStrap {
def init = { servletContext ->
println "replacing onChangeListener"
def ctx = servletContext.getAttribute(GrailsApplicationAttributes.APPLICATION_CONTEXT)
ctx.pluginManager.getGrailsPlugin('hibernate').onChangeListener = PatchedHibernatePluginSupport.onChange
}
def destroy = {
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment