DefaultApplication.groovy
001 /*
002  * SPDX-License-Identifier: Apache-2.0
003  *
004  * Copyright 2020 Vladimir Orany.
005  *
006  * Licensed under the Apache License, Version 2.0 (the "License");
007  * you may not use this file except in compliance with the License.
008  * You may obtain a copy of the License at
009  *
010  *     https://www.apache.org/licenses/LICENSE-2.0
011  *
012  * Unless required by applicable law or agreed to in writing, software
013  * distributed under the License is distributed on an "AS IS" BASIS,
014  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015  * See the License for the specific language governing permissions and
016  * limitations under the License.
017  */
018 package micronaut.grails.example
019 
020 import com.agorapulse.micronaut.grails.MicronautGrailsApp
021 import com.agorapulse.micronaut.grails.domain.Manager
022 import grails.boot.GrailsApp
023 import grails.boot.config.GrailsAutoConfiguration
024 import groovy.transform.stc.ClosureParams
025 import groovy.transform.stc.SimpleType
026 import groovy.util.logging.Slf4j
027 import io.micronaut.context.ApplicationContextConfiguration
028 import io.micronaut.context.DefaultApplicationContext
029 import io.micronaut.context.env.DefaultEnvironment
030 import io.micronaut.context.env.Environment
031 import io.micronaut.core.reflect.ClassUtils
032 import io.micronaut.spring.context.factory.MicronautBeanFactoryConfiguration
033 import org.grails.core.util.BeanCreationProfilingPostProcessor
034 import org.springframework.beans.BeanUtils
035 import org.springframework.boot.Banner
036 import org.springframework.boot.WebApplicationType
037 import org.springframework.context.ConfigurableApplicationContext
038 import org.springframework.core.convert.ConversionService
039 import org.springframework.core.env.ConfigurableEnvironment
040 import org.springframework.core.env.PropertyResolver
041 import org.springframework.core.io.ResourceLoader
042 
043 class DefaultApplication extends GrailsAutoConfiguration {
044 
045     static ConfigurableApplicationContext context
046 
047     static void main(String[] args) {
048         context = CustomGrailsApp.run(DefaultApplication, args) { env ->
049             env.addPackage(Manager.package)
050         }
051     }
052 
053     @Slf4j
054     static class CustomGrailsApp extends GrailsApp {
055 
056         private final Closure<Environment> configureMicronautEnvironment
057 
058         // copy pasted
059 
060         /**
061          * Static helper that can be used to run a {@link GrailsApp} from the
062          * specified source using default settings.
063          @param source the source to load
064          @param args the application arguments (usually passed from a Java main method)
065          @return the running {@link org.springframework.context.ApplicationContext}
066          */
067         static ConfigurableApplicationContext run(
068             Class<?> source,
069             String[] args,
070             @ClosureParams(value = SimpleType.class, options = 'io.micronaut.context.env.Environment')
071                 Closure<Environment> configureMicronautEnvironment
072         ) {
073             return run([sourceas Class[], args, configureMicronautEnvironment)
074         }
075 
076 
077         /**
078          * Static helper that can be used to run a {@link GrailsApp} from the
079          * specified sources using default settings and user supplied arguments.
080          @param sources the sources to load
081          @param args the application arguments (usually passed from a Java main method)
082          @return the running {@link org.springframework.context.ApplicationContext}
083          */
084         static ConfigurableApplicationContext run(
085             Class<?>[] sources,
086             String[] args,
087             @ClosureParams(value = SimpleType.class, options = 'io.micronaut.context.env.Environment')
088                 Closure<Environment> configureMicronautEnvironment
089         ) {
090             CustomGrailsApp grailsApp = new CustomGrailsApp(configureMicronautEnvironment, sources)
091             grailsApp.bannerMode = Banner.Mode.OFF
092             return grailsApp.run(args)
093         }
094 
095         CustomGrailsApp(@ClosureParams(value = SimpleType.class, options = 'io.micronaut.context.env.Environment') Closure<Environment> configureMicronautEnvironment, Class<?>... sources) {
096             super(sources)
097             this.configureMicronautEnvironment = configureMicronautEnvironment
098         }
099 
100         CustomGrailsApp(ResourceLoader resourceLoader, @ClosureParams(value = SimpleType.class, options = 'io.micronaut.context.env.Environment') Closure<Environment> configureMicronautEnvironment, Class<?>... sources) {
101             super(resourceLoader, sources)
102             this.configureMicronautEnvironment = configureMicronautEnvironment
103         }
104 
105 
106         /**
107          * Strategy method used to create the {@link org.springframework.context.ApplicationContext}. By default this
108          * method will respect any explicitly set application context or application context
109          * class before falling back to a suitable default.
110          @return the application context (not yet refreshed)
111          @see #setApplicationContextClass(Class)
112          */
113         protected ConfigurableApplicationContext createSpringApplicationContext() {
114             Class<?> contextClass = null
115             try {
116                 switch (this.webApplicationType) {
117                     case WebApplicationType.SERVLET:
118                         contextClass = Class.forName(MicronautGrailsApp.DEFAULT_SERVLET_WEB_CONTEXT_CLASS)
119                         break
120                     case WebApplicationType.REACTIVE:
121                         contextClass = Class.forName(MicronautGrailsApp.DEFAULT_REACTIVE_WEB_CONTEXT_CLASS)
122                         break
123                     default:
124                         contextClass = Class.forName(MicronautGrailsApp.DEFAULT_CONTEXT_CLASS)
125                 }
126             }
127             catch (ClassNotFoundException ex) {
128                 throw new IllegalStateException(
129                     "Unable create a default ApplicationContext, " "please specify an ApplicationContextClass",
130                     ex)
131             }
132             return (ConfigurableApplicationContextBeanUtils.instantiateClass(contextClass)
133         }
134 
135         @Override
136         protected ConfigurableApplicationContext createApplicationContext() {
137             setAllowBeanDefinitionOverriding(true)
138             ConfigurableApplicationContext applicationContext = createSpringApplicationContext()
139             def now = System.currentTimeMillis()
140 
141             ClassLoader applicationClassLoader = GrailsApp.classLoader
142             ApplicationContextConfiguration micronautConfiguration = new ApplicationContextConfiguration() {
143                 @Override
144                 List<String> getEnvironments() {
145                     if (configuredEnvironment != null) {
146                         return configuredEnvironment.getActiveProfiles().toList()
147                     else {
148                         return Collections.emptyList()
149                     }
150                 }
151 
152                 @Override
153                 Optional<Boolean> getDeduceEnvironments() {
154                     return Optional.of(false)
155                 }
156 
157                 @Override
158                 ClassLoader getClassLoader() {
159                     return applicationClassLoader
160                 }
161             }
162 
163             List beanExcludes = []
164             beanExcludes.add(ConversionService.class)
165             beanExcludes.add(org.springframework.core.env.Environment.class)
166             beanExcludes.add(PropertyResolver.class)
167             beanExcludes.add(ConfigurableEnvironment.class)
168             def objectMapper = ClassUtils.forName("com.fasterxml.jackson.databind.ObjectMapper", classLoader).orElse(null)
169             if (objectMapper != null) {
170                 beanExcludes.add(objectMapper)
171             }
172             def micronautContext = new DefaultApplicationContext(micronautConfiguration) {
173 
174                 @Override
175                 protected DefaultEnvironment createEnvironment(ApplicationContextConfiguration configuration) {
176                     return configureMicronautEnvironment.call(super.createEnvironment(configuration)) as DefaultEnvironment
177                 }
178             }
179 
180             micronautContext
181                 .environment
182                 .addPropertySource("grails-config"[(MicronautBeanFactoryConfiguration.PREFIX + ".bean-excludes")(ObjectbeanExcludes])
183             micronautContext.start()
184 
185             ConfigurableApplicationContext parentContext = micronautContext.getBean(ConfigurableApplicationContext)
186             applicationContext.setParent(
187                 parentContext
188             )
189             applicationContext.addApplicationListener(new MicronautShutdownListener(micronautContext))
190             log.info("Started Micronaut Parent Application Context in ${System.currentTimeMillis() - now}ms")
191 
192 
193             if (enableBeanCreationProfiler) {
194                 def processor = new BeanCreationProfilingPostProcessor()
195                 applicationContext.getBeanFactory().addBeanPostProcessor(processor)
196                 applicationContext.addApplicationListener(processor)
197             }
198             return applicationContext
199         }
200     }
201 
202 }