Created
September 28, 2012 19:59
-
-
Save lhotari/3801817 to your computer and use it in GitHub Desktop.
GRAILS-9411 and GRAILS-9412 monkey patch for Grails 2.1.1
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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" | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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