Skip to content

Commit

Permalink
Auto-Configure Hibernate Plugin
Browse files Browse the repository at this point in the history
Closes gh-5
  • Loading branch information
rainboyan committed Oct 6, 2024
1 parent af2e8bd commit 429b4af
Show file tree
Hide file tree
Showing 4 changed files with 193 additions and 29 deletions.
1 change: 1 addition & 0 deletions grace-plugin/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ dependencies {
documentation ("org.graceframework:grace-spring:$graceVersion")
documentation ("com.github.javaparser:javaparser-core:$javaParserCoreVersion")

implementation("org.springframework.boot:spring-boot-autoconfigure:$springBootVersion")
compileOnly("org.graceframework:grace-boot:$graceVersion")
compileOnly("org.graceframework:grace-bootstrap:$graceVersion")
compileOnly "org.graceframework:grace-core:$graceVersion", {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
/*
* Copyright 2024 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
*
* https://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 grails.plugin.hibernate;

import java.beans.Introspector;
import java.util.HashSet;
import java.util.Set;

import javax.sql.DataSource;

import org.hibernate.SessionFactory;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigureOrder;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
import org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.core.annotation.Order;

import grails.config.Config;
import grails.core.GrailsApplication;
import grails.core.GrailsClass;
import org.grails.core.artefact.DomainClassArtefactHandler;
import org.grails.datastore.gorm.events.ConfigurableApplicationContextEventPublisher;
import org.grails.datastore.gorm.proxy.ProxyHandlerAdapter;
import org.grails.datastore.mapping.model.MappingContext;
import org.grails.datastore.mapping.services.Service;
import org.grails.orm.hibernate.HibernateDatastore;
import org.grails.orm.hibernate.proxy.HibernateProxyHandler;
import org.grails.plugin.hibernate.support.AggregatePersistenceContextInterceptor;
import org.grails.plugin.hibernate.support.GrailsOpenSessionInViewInterceptor;
import org.grails.transaction.ChainedTransactionManagerPostProcessor;

/**
* {@link EnableAutoConfiguration Auto-Configure} for GORM for Hibernate
*
* @author Michael Yan
* @since 2023.1
*/
@AutoConfiguration(after = DataSourceAutoConfiguration.class,
before = { HibernateJpaAutoConfiguration.class, TransactionAutoConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class})
@AutoConfigureOrder
@ConditionalOnClass(HibernateDatastore.class)
public class HibernateGormAutoConfiguration {

private static final String TRANSACTION_MANAGER_WHITE_LIST_PATTERN = "grails.transaction.chainedTransactionManager.whitelistPattern";
private static final String TRANSACTION_MANAGER_BLACK_LIST_PATTERN = "grails.transaction.chainedTransactionManager.blacklistPattern";

private final ConfigurableApplicationContext applicationContext;
private final ObjectProvider<GrailsApplication> grailsApplication;

public HibernateGormAutoConfiguration(ApplicationContext applicationContext,
ObjectProvider<GrailsApplication> grailsApplication) {
this.applicationContext = (ConfigurableApplicationContext) applicationContext;
this.grailsApplication = grailsApplication;
}

@Bean
@ConditionalOnMissingBean
public HibernateDatastore hibernateDatastore(ObjectProvider<DataSource> dataSource) {
GrailsClass[] grailsClasses = this.grailsApplication.getObject().getArtefacts(DomainClassArtefactHandler.TYPE);
Set<Class<?>> domainClasses = new HashSet<>();
for (GrailsClass grailsClass : grailsClasses) {
if (grailsClass.getClazz() != null) {
domainClasses.add(grailsClass.getClazz());
}
}

HibernateDatastore datastore;
if (dataSource.getIfAvailable() != null) {
datastore = new HibernateDatastore(
dataSource.getObject(),
this.applicationContext.getEnvironment(),
new ConfigurableApplicationContextEventPublisher(this.applicationContext),
domainClasses.toArray(new Class[0])
);
}
else {
datastore = new HibernateDatastore(
this.applicationContext.getEnvironment(),
new ConfigurableApplicationContextEventPublisher(this.applicationContext),
domainClasses.toArray(new Class[0])
);
}

for (Service<?> service : datastore.getServices()) {
Class<?> serviceClass = service.getClass();
grails.gorm.services.Service ann = serviceClass.getAnnotation(grails.gorm.services.Service.class);
String serviceName;
if (ann == null) {
serviceName = Introspector.decapitalize(serviceClass.getSimpleName());
}
else {
serviceName = ann.name();
}
if (!this.applicationContext.containsBean(serviceName)) {
this.applicationContext.getBeanFactory().registerSingleton(
serviceName,
service
);
}
}
return datastore;
}

@Bean
@ConditionalOnMissingBean
public SessionFactory sessionFactory(HibernateDatastore hibernateDatastore) {
return hibernateDatastore.getSessionFactory();
}

@Bean
@ConditionalOnMissingBean
public MappingContext grailsDomainClassMappingContext(HibernateDatastore hibernateDatastore) {
return hibernateDatastore.getMappingContext();
}

@Bean
@ConditionalOnMissingBean
public AggregatePersistenceContextInterceptor persistenceInterceptor(HibernateDatastore hibernateDatastore) {
return new AggregatePersistenceContextInterceptor(hibernateDatastore);
}

@Bean
@ConditionalOnMissingBean
public HibernateProxyHandler hibernateProxyHandler() {
return new HibernateProxyHandler();
}

@Bean
@Order(10)
@ConditionalOnMissingBean
public ProxyHandlerAdapter proxyHandler(HibernateProxyHandler hibernateProxyHandler) {
return new ProxyHandlerAdapter(hibernateProxyHandler);
}

@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(name = "hibernate.osiv.enabled", havingValue = "true", matchIfMissing = true)
public GrailsOpenSessionInViewInterceptor openSessionInViewInterceptor(HibernateDatastore hibernateDatastore) {
GrailsOpenSessionInViewInterceptor openSessionInViewInterceptor = new GrailsOpenSessionInViewInterceptor();
openSessionInViewInterceptor.setHibernateDatastore(hibernateDatastore);
return openSessionInViewInterceptor;
}

@Bean
@ConditionalOnProperty(prefix = "grails.transaction.chainedTransactionManager", name = "enabled", havingValue = "true")
public ChainedTransactionManagerPostProcessor chainedTransactionManagerPostProcessor() {
Config config = this.grailsApplication.getObject().getConfig();
String whitelistPattern = config.getProperty(TRANSACTION_MANAGER_WHITE_LIST_PATTERN, "");
String blacklistPattern = config.getProperty(TRANSACTION_MANAGER_BLACK_LIST_PATTERN, "");
return new ChainedTransactionManagerPostProcessor(config, whitelistPattern, blacklistPattern);
}

}
Original file line number Diff line number Diff line change
@@ -1,21 +1,30 @@
/*
* Copyright 2016-2024 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
*
* https://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 grails.plugin.hibernate

import groovy.transform.CompileStatic
import org.springframework.beans.factory.support.BeanDefinitionRegistry
import org.springframework.context.ConfigurableApplicationContext
import org.springframework.core.convert.converter.Converter
import org.springframework.core.convert.support.ConfigurableConversionService
import org.springframework.core.env.PropertyResolver

import grails.config.Config
import grails.core.GrailsApplication
import grails.core.GrailsClass
import grails.orm.bootstrap.HibernateDatastoreSpringInitializer
import grails.plugins.Plugin
import grails.util.Environment

import org.grails.config.PropertySourcesConfig
import org.grails.core.artefact.DomainClassArtefactHandler

/**
* Plugin that integrates Hibernate into a Grails application
Expand All @@ -27,8 +36,6 @@ import org.grails.core.artefact.DomainClassArtefactHandler
@CompileStatic
class HibernateGrailsPlugin extends Plugin {

public static final String DEFAULT_DATA_SOURCE_NAME = HibernateDatastoreSpringInitializer.DEFAULT_DATA_SOURCE_NAME

def grailsVersion = '2023.0.0 > *'

def author = 'Grace Framework'
Expand All @@ -46,8 +53,6 @@ class HibernateGrailsPlugin extends Plugin {
def issueManagement = [system: 'Github', url: 'https://github.com/graceframework/grace-data-hibernate/issues']
def scm = [url: 'https://github.com/graceframework/grace-data-hibernate']

Set<String> dataSourceNames

Closure doWithSpring() {
{ ->
ConfigurableApplicationContext applicationContext = (ConfigurableApplicationContext) applicationContext
Expand All @@ -65,26 +70,7 @@ class HibernateGrailsPlugin extends Plugin {
})
((PropertySourcesConfig) config).setConversionService(conversionService)
}


def domainClasses = grailsApplication.getArtefacts(DomainClassArtefactHandler.TYPE)
.collect() { GrailsClass cls -> cls.clazz }

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)

beans.delegate = delegate
beans.call()
}
}

@Override
void onChange(Map<String, Object> event) {
// TODO: rewrite onChange handling
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
grails.plugin.hibernate.HibernateGormAutoConfiguration

0 comments on commit 429b4af

Please sign in to comment.