Skip to content

Commit 9d1b732

Browse files
committed
Migrate mongodb plugin.doWithSpring() to Spring Boot AutoConfiguration
Closes gh-8
1 parent e9a9480 commit 9d1b732

File tree

5 files changed

+263
-79
lines changed

5 files changed

+263
-79
lines changed

grace-plugin/build.gradle

Lines changed: 6 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -8,28 +8,11 @@ dependencies {
88
exclude group: 'org.hamcrest', module: 'hamcrest-core'
99
}
1010

11+
api("org.springframework.boot:spring-boot-autoconfigure:$springBootVersion")
12+
annotationProcessor("org.springframework.boot:spring-boot-autoconfigure-processor:$springBootVersion")
13+
annotationProcessor("org.springframework.boot:spring-boot-configuration-processor:$springBootVersion")
14+
api "org.graceframework:grace-datastore-gorm-support:$datastoreVersion"
1115
api "org.graceframework:grace-datastore-web:$datastoreVersion"
12-
api "org.graceframework:grace-datastore-gorm-support:$datastoreVersion", {
13-
exclude group:'org.springframework', module:'spring-context'
14-
exclude group:'org.springframework', module:'spring-core'
15-
exclude group:'org.springframework', module:'spring-beans'
16-
exclude group:'org.springframework', module:'spring-tx'
17-
exclude group:'org.graceframework', module:'grace-bootstrap'
18-
// exclude group:'org.codehaus.groovy', module:'groovy-all'
19-
exclude group:'org.graceframework', module:'grace-core'
20-
exclude group:'javax.transaction', module:'jta'
21-
}
22-
api project(":grace-datastore-gorm-mongodb-ext"), {
23-
exclude group:'org.graceframework', module:'grace-datastore-gorm-mongodb'
24-
}
25-
api project(":grace-datastore-gorm-mongodb"), {
26-
exclude group:'org.springframework', module:'spring-context'
27-
exclude group:'org.springframework', module:'spring-core'
28-
exclude group:'org.springframework', module:'spring-beans'
29-
exclude group:'org.springframework', module:'spring-tx'
30-
exclude group:'org.graceframework', module:'grace-bootstrap'
31-
exclude group:'org.codehaus.groovy', module:'groovy-all'
32-
exclude group:'org.graceframework', module:'grace-core'
33-
exclude group:'javax.transaction', module:'jta'
34-
}
16+
api project(":grace-datastore-gorm-mongodb-ext")
17+
api project(":grace-datastore-gorm-mongodb")
3518
}
Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
/*
2+
* Copyright 2025 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package grails.plugins.mongodb;
17+
18+
import java.beans.Introspector;
19+
import java.util.Collections;
20+
import java.util.HashSet;
21+
import java.util.Set;
22+
23+
import com.mongodb.client.MongoClient;
24+
import org.springframework.beans.BeansException;
25+
import org.springframework.beans.factory.ObjectProvider;
26+
import org.springframework.boot.autoconfigure.AutoConfiguration;
27+
import org.springframework.boot.autoconfigure.AutoConfigureOrder;
28+
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
29+
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
30+
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
31+
import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration;
32+
import org.springframework.context.ApplicationContext;
33+
import org.springframework.context.ApplicationContextAware;
34+
import org.springframework.context.ConfigurableApplicationContext;
35+
import org.springframework.context.annotation.Bean;
36+
import org.springframework.context.annotation.Primary;
37+
import org.springframework.core.env.ConfigurableEnvironment;
38+
import org.springframework.transaction.PlatformTransactionManager;
39+
40+
import grails.boot.config.GrailsComponentScanner;
41+
import grails.core.GrailsApplication;
42+
import grails.core.GrailsClass;
43+
import org.grails.core.artefact.DomainClassArtefactHandler;
44+
import org.grails.datastore.gorm.events.AutoTimestampEventListener;
45+
import org.grails.datastore.gorm.events.ConfigurableApplicationContextEventPublisher;
46+
import org.grails.datastore.gorm.plugin.support.PersistenceContextInterceptorAggregator;
47+
import org.grails.datastore.gorm.support.DatastorePersistenceContextInterceptor;
48+
import org.grails.datastore.mapping.model.MappingContext;
49+
import org.grails.datastore.mapping.mongo.MongoDatastore;
50+
import org.grails.datastore.mapping.services.Service;
51+
import org.grails.datastore.mapping.web.support.OpenSessionInViewInterceptor;
52+
53+
/**
54+
* {@link EnableAutoConfiguration Auto-Configure} for GORM for MongoDB
55+
*
56+
* @author Michael Yan
57+
* @since 2023.3
58+
*/
59+
@AutoConfigureOrder(300)
60+
@AutoConfiguration(after = MongoAutoConfiguration.class)
61+
@ConditionalOnMissingBean(MongoDatastore.class)
62+
public class MongoDbGormAutoConfiguration implements ApplicationContextAware {
63+
64+
private ConfigurableApplicationContext applicationContext;
65+
66+
@Bean
67+
@Primary
68+
@ConditionalOnMissingBean
69+
public MongoDatastore mongoDatastore(ObjectProvider<MongoClient> mongo, ObjectProvider<GrailsApplication> grailsApplication) {
70+
GrailsClass[] grailsClasses = grailsApplication.getObject().getArtefacts(DomainClassArtefactHandler.TYPE);
71+
Set<Class<?>> domainClasses = new HashSet<>();
72+
for (GrailsClass grailsClass : grailsClasses) {
73+
if (grailsClass.getClazz() != null) {
74+
domainClasses.add(grailsClass.getClazz());
75+
}
76+
}
77+
78+
GrailsComponentScanner scanner = new GrailsComponentScanner(this.applicationContext);
79+
Set<Class<?>> entityClasses;
80+
try {
81+
entityClasses = scanner.scan(grails.gorm.annotation.Entity.class, grails.persistence.Entity.class);
82+
}
83+
catch (ClassNotFoundException ignored) {
84+
entityClasses = Collections.emptySet();
85+
}
86+
87+
domainClasses.addAll(entityClasses);
88+
89+
ConfigurableEnvironment environment = this.applicationContext.getEnvironment();
90+
ConfigurableApplicationContextEventPublisher eventPublisher = new ConfigurableApplicationContextEventPublisher(this.applicationContext);
91+
MongoDatastore datastore;
92+
if (mongo.getIfAvailable() != null) {
93+
datastore = new MongoDatastore(mongo.getObject(), environment, eventPublisher, domainClasses.toArray(new Class[0]));
94+
}
95+
else {
96+
datastore = new MongoDatastore(environment, eventPublisher, domainClasses.toArray(new Class[0]));
97+
}
98+
99+
for (Service<?> service : datastore.getServices()) {
100+
Class<?> serviceClass = service.getClass();
101+
grails.gorm.services.Service ann = serviceClass.getAnnotation(grails.gorm.services.Service.class);
102+
String serviceName;
103+
if (ann == null) {
104+
serviceName = Introspector.decapitalize(serviceClass.getSimpleName());
105+
}
106+
else {
107+
serviceName = ann.name();
108+
}
109+
if (!this.applicationContext.containsBean(serviceName)) {
110+
this.applicationContext.getBeanFactory().registerSingleton(
111+
serviceName,
112+
service
113+
);
114+
}
115+
}
116+
117+
return datastore;
118+
}
119+
120+
@Bean
121+
@ConditionalOnMissingBean
122+
public MappingContext mongoMappingContext(MongoDatastore mongoDatastore) {
123+
return mongoDatastore.getMappingContext();
124+
}
125+
126+
@Bean
127+
@ConditionalOnMissingBean
128+
public AutoTimestampEventListener mongoAutoTimestampEventListener(MongoDatastore mongoDatastore) {
129+
return mongoDatastore.getAutoTimestampEventListener();
130+
}
131+
132+
@Bean
133+
@ConditionalOnMissingBean
134+
public DatastorePersistenceContextInterceptor datastorePersistenceContextInterceptor(MongoDatastore mongoDatastore) {
135+
return new DatastorePersistenceContextInterceptor(mongoDatastore);
136+
}
137+
138+
@Bean
139+
@ConditionalOnMissingBean
140+
public PersistenceContextInterceptorAggregator persistenceContextInterceptorAggregator() {
141+
return new PersistenceContextInterceptorAggregator();
142+
}
143+
144+
@Bean({"mongoTransactionManager", "transactionManager"})
145+
@Primary
146+
@ConditionalOnMissingBean
147+
public PlatformTransactionManager transactionManager(MongoDatastore mongoDatastore) {
148+
return mongoDatastore.getTransactionManager();
149+
}
150+
151+
@Bean
152+
@ConditionalOnMissingBean
153+
@ConditionalOnClass(name = "org.grails.datastore.mapping.web.support.OpenSessionInViewInterceptor")
154+
public OpenSessionInViewInterceptor mongoOpenSessionInViewInterceptor(MongoDatastore mongoDatastore) {
155+
OpenSessionInViewInterceptor interceptor = new OpenSessionInViewInterceptor();
156+
interceptor.setDatastore(mongoDatastore);
157+
return interceptor;
158+
}
159+
160+
@Override
161+
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
162+
if (!(applicationContext instanceof ConfigurableApplicationContext)) {
163+
throw new IllegalArgumentException("MongoDbGormAutoConfiguration requires an instance of ConfigurableApplicationContext");
164+
}
165+
this.applicationContext = (ConfigurableApplicationContext) applicationContext;
166+
}
167+
168+
}
Lines changed: 37 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,67 +1,48 @@
1+
/*
2+
* Copyright 2016-2025 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
116
package grails.plugins.mongodb
217

3-
import grails.core.GrailsClass
4-
import grails.mongodb.bootstrap.MongoDbDataStoreSpringInitializer
5-
import grails.plugins.GrailsPlugin
6-
import grails.plugins.Plugin
7-
import grails.util.Metadata
818
import groovy.transform.CompileStatic
9-
import org.grails.core.artefact.DomainClassArtefactHandler
10-
import org.grails.datastore.gorm.plugin.support.ConfigSupport
11-
import org.grails.datastore.mapping.mongo.MongoDatastore
12-
import org.springframework.beans.factory.support.BeanDefinitionRegistry
13-
import org.springframework.context.ConfigurableApplicationContext
14-
import org.springframework.core.env.PropertyResolver
15-
import org.springframework.transaction.PlatformTransactionManager
16-
17-
class MongodbGrailsPlugin extends Plugin {
18-
def license = "Apache 2.0 License"
19-
def organization = [name: "Grails", url: "https://grails.org/"]
20-
def developers = [
21-
[name: "Puneet Behl", email: "[email protected]"]]
22-
def issueManagement = [system: "Github", url: "https://github.com/grails/gorm-mongodb"]
23-
def scm = [url: "https://github.com/grails/gorm-mongodb"]
24-
25-
def grailsVersion = "6.0.0 > *"
26-
def observe = ['services', 'domainClass']
27-
def loadAfter = ['domainClass', 'hibernate', 'hibernate4', 'services']
28-
def author = "Puneet Behl"
29-
def authorEmail = "[email protected]"
30-
def title = "GORM MongoDB"
31-
def description = 'A plugin that integrates the MongoDB document datastore into the Grails framework, providing a GORM API onto it'
3219

33-
def documentation = "https://gorm.grails.org/latest/mongodb/manual/"
34-
35-
@Override
36-
@CompileStatic
37-
Closure doWithSpring() {
38-
ConfigSupport.prepareConfig(config, (ConfigurableApplicationContext) applicationContext)
39-
def initializer = new MongoDbDataStoreSpringInitializer((PropertyResolver) config, grailsApplication.getArtefacts(DomainClassArtefactHandler.TYPE).collect() { GrailsClass cls -> cls.clazz })
40-
initializer.registerApplicationIfNotPresent = false
20+
import grails.plugins.Plugin
4121

42-
def applicationName = Metadata.getCurrent().getApplicationName()
43-
if(!applicationName.contains('@')) {
44-
initializer.databaseName = applicationName
45-
}
46-
initializer.setSecondaryDatastore(hasHibernatePlugin())
22+
/**
23+
* Plugin that integrates Hibernate into a Grails application
24+
*
25+
* @author Graeme Rocher
26+
* @author Puneet Behl
27+
* @author Michael Yan
28+
* @since 3.0
29+
*/
30+
@CompileStatic
31+
class MongodbGrailsPlugin extends Plugin {
4732

48-
return initializer.getBeanDefinitions((BeanDefinitionRegistry)applicationContext)
49-
}
33+
def grailsVersion = '2023.0.0 > *'
5034

51-
@CompileStatic
52-
protected boolean hasHibernatePlugin() {
53-
manager.allPlugins.any() { GrailsPlugin plugin -> plugin.name ==~ /hibernate\d*/}
54-
}
35+
def author = 'Grace Framework'
36+
def title = 'Grace Data MongoDB'
37+
def description = 'Provides integration between Grace and MongoDB document datastore through GORM API'
38+
def documentation = 'https://github.com/graceframework/grace-data-mongodb'
5539

56-
@Override
57-
@CompileStatic
58-
void onChange(Map<String, Object> event) {
40+
def observe = ['domainClass']
41+
def loadAfter = ['domainClass']
5942

60-
def ctx = applicationContext
61-
event.application = grailsApplication
62-
event.ctx = applicationContext
43+
def license = 'APACHE'
44+
def organization = [name: 'Grace Framework', url: 'https://graceframework.org']
45+
def issueManagement = [system: 'Github', url: 'https://github.com/graceframework/grace-data-mongodb/issues']
46+
def scm = [url: 'https://github.com/graceframework/grace-data-mongodb']
6347

64-
def mongoDatastore = ctx.getBean(MongoDatastore)
65-
def mongoTransactionManager = ctx.getBean('mongoTransactionManager', PlatformTransactionManager)
66-
}
6748
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
{
2+
"groups": [],
3+
"properties": [
4+
{
5+
"name": "grails.gorm.flushMode",
6+
"type": "java.lang.String",
7+
"description": "The flush mode to use.",
8+
"defaultValue": "COMMIT"
9+
},
10+
{
11+
"name": "grails.gorm.failOnError",
12+
"type": "java.lang.Boolean",
13+
"description": "Whether to throw an exception on validation error.",
14+
"defaultValue": false
15+
},
16+
{
17+
"name": "grails.gorm.default.mapping",
18+
"type": "java.lang.String",
19+
"description": "The default mapping to apply to all classes."
20+
},
21+
{
22+
"name": "grails.gorm.default.constraints",
23+
"type": "java.lang.String",
24+
"description": "The default constraints to apply to all classes."
25+
},
26+
{
27+
"name": "grails.gorm.multiTenancy.mode",
28+
"type": "java.lang.String",
29+
"description": "The multi tenancy mode.",
30+
"defaultValue": "NONE"
31+
},
32+
{
33+
"name": "grails.transaction.chainedTransactionManager.blacklistPattern",
34+
"type": "java.lang.String",
35+
"description": "The pattern of transaction manager bean names to deny to register."
36+
},
37+
{
38+
"name": "grails.transaction.chainedTransactionManager.whitelistPattern",
39+
"type": "java.lang.String",
40+
"description": "The pattern of transaction manager bean names to allow to register.",
41+
"defaultValue": "(?i).*transactionManager(_.+)?"
42+
},
43+
{
44+
"name": "grails.transaction.chainedTransactionManager.enabled",
45+
"type": "java.lang.Boolean",
46+
"description": "Wheather to enable chained transaction management.",
47+
"defaultValue": false
48+
}
49+
],
50+
"hints": []
51+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
grails.plugins.mongodb.MongoDbGormAutoConfiguration

0 commit comments

Comments
 (0)