Accessing environment properties in Application.doWithSpring() - spring

I am adding a URL specific health check to my Application file, using doWithSpring().
I have the URL specified in an environment.properties file, and it is loaded into 'grailApplication.config.'
My problem is that 'grailsApplication' and 'config' have not been initialized yet.
How do I get to the values that will be located in these variables.
Can I move the health check creation to doWithApplicationContext()?

You can use GrailsConfigurationAware interface to let grails inject grails configuration object into spring beans.
sample code
class HealthChecker implements GrailsConfigurationAware {
def checkUrlHealth() {
}
#Override
void setConfiguration(Config config) {
// TODO with grails configurations
}
}
And then register it as a spring bean by edit conf/resources.groovy
import chess_api.HealthChecker
// Place your Spring DSL code here
beans = {
healthChecker(HealthChecker)
}
Then after spring initialize the grails and your bean, the "setConfiguration" method will be called with grails configurations.
Be aware this method will not work for service, you have to explicitly register the spring bean.

Related

Inject/Access Spring Bean into Log4j2 Plugin

I have a configuration properties class that I want to inject into a custom log4j2 RewritePolicy.
e.g.
#Plugin(name = "MyPolicy", category = "Core", elementType = "rewritePolicy", printObject = true)
public class MyPolicy implements RewritePolicy {
private MyPolicyProperties myPolicyProperties; // <-- want to inject/autowire this
public MyPolicy() {}
#PluginFactory
public static MyPolicy createPolicy() {
return new MyPolicy();
}
#Override
public LogEvent rewrite(LogEvent logEvent) {
// do something with myPolicyProperties here
return Log4jLogEvent.newBuilder()
.setLoggerName(logEvent.getLoggerName())
.setMarker(logEvent.getMarker())
.setLoggerFqcn(logEvent.getLoggerFqcn())
// ... etc
.build();
}
}
#ConfigurationProperties("app.mypolicy")
#Getter
#Setter
public class MyPolicyProperties {
private String property1;
private int property2;
// ... etc
}
I've tried implementing an ApplicationListener to reconfigure log4j as described here but was can't seem to get the appender and/or rewritepolicy to configure. Also tried implementing ApplicationContextAware described here but also didn't work.
Is there anyway to access the MyPolicyProperties in MyPolicy?
It can be done but it is almost never pretty. This is because Log4j Plugins are loaded by Log4j's plugin system while Spring Beans are loaded by Spring. Furthermore, they are not instantiated at the same time.
If you are using Spring Boot the very first thing that will happen is for Log4j2 to initialize because SpringApplication requests a Logger. So there would be no way to resolve the Spring Bean at that point as it doesn't exist. Later, Spring's bootstrap process will initialize Log4j again and then during application setup it will initialize once or twice more. During these subsequent initializations the bean may be available.
Depending on the type of application you are using you may be able to locate Spring's ApplicationContext so that you can call getBean() and inject it.
There is no automatic way to do this via an annotation or something similar.
The simplest way to do it is to either add a static method in the target class that gets initialized to reference itself when Spring is initialized or to create another class with a method that initializes a static method to reference the Spring created bean. So Spring will cause these static methods to reference the bean it creates. Then have your Log4j plugin call that static method to get the bean reference. Once it is non-null you can save it in the plugin and after that it should function as you want.

Adding legacy singleton to Spring ApplicationContext for Injection

I am trying to create a lightweight web service around a legacy java library to expose it as a web service using Spring Boot. I am new to Spring, while I have a lot of java experiance writing libraries all my web service experiance is in ASP.NET.
I can instantiate an instance of my library object but I can't figure out how to then have that object be injected into my controllers via #Autowired when the application is spun up.
This is my main application:
#SpringBootApplication
public class ResolverWebServiceApplication {
private static ArgumentParser newArgumentParser() {
ArgumentParser parser = ArgumentParsers.newFor("Resolver").build();
// configuring the parser
return parser;
}
public static void main(String[] args) throws ArgumentParserException {
ArgumentParser parser = newArgumentParser();
Namespace ns = parser.parseArgs(args);
ResolverOptions options = new ResolverOptions.Builder(ns)
.build();
ResolverContext context = new ResolverContext(options);
// ^^^ I need to get this injected into my controllers ^^^
SpringApplication.run(ResolverWebServiceApplication.class, args);
}
}
And then a simple controller which needs the class injected:
#RestController
public class VersionController {
#Autowired
private ResolverContext context; // And here the instance needs to be injected.
#GetMapping(path = "/version", produces = MediaType.APPLICATION_JSON_VALUE)
public long version() {
return context.getResolver().getVersionAsLong();
}
}
I could make the context a singleton which the controllers just refer to but I want to be able to test my controllers by mocking the context. There is also obviously a lot of validation and error handeling that needs to be added.
I can't have it be a Bean since I only want to instantiate one for my entire application.
The closest question I have found is this one: Registering an instance as 'singleton' bean at application startup. But I can't put the options in the configuration files. The application might be spun up in a container or on a users machine and requires the ability to accept arguments to initialize the library class. It would be a real usability degradation if someone had to manually edit the application config for these options.
You need to tell spring to consider the required classes from your lib when initializing the application context i.e Configure and let spring know how to create a bean and then let spring handle dependency injection for you.
First of all, add required jar that you have in your build file, say pom.xml for maven, in your current project. Idea is to have it on your classpath when you build the project.
As you said it is legacy lib and I am assuming it is not a spring bean, then
In your configuration class, return it as a bean, using #Bean annotaion.
#Configuration
public class YourConfigurationClass {
#Bean
SomeBean returnSomeBeanFromLegacyLib() {
return new SomeClassFromLib();
}
Once you return this bean from your config, it should be available to Spring Context for dependency injection whereever you #Autowire the required dependency.

Bean overriding in Spring context that uses both annotation and xml config

There is a spring project A which is completely annotation based.
I need to override some beans conditionally in project B which is a legacy application using Spring 4.1.3 and uses xml based config.
There is FooConfig which is configuring beans using #ComponentScan. This config is a third party code for me. i.e I do not have access for this
#ComponentScan(basePackages = {"com.foo.bean"})
#Configuration
public class FooConfig {
}
I have created a BarConfig at my end, which imports this FooConfig and overrides some beans based on a condition. This is achieved using #Conditional
#Configuration
#Import(FooConfig.class)
public class BarConfig {
#Bean(name="helloService")
#Conditional(IsSpanishCondition.class)
public HelloService getHelloService() {
return new HelloService() {
#Override
public String getGreeting(String name) {
return "Hola "+name;
}
};
}
}
And I have included BarConfig in my application-context.xml
<context:annotation-config/>
<bean class="com.foo.config.BarConfig"/>
While this approach works flawlessly in Spring 5.1.2.RELEASE, it does not work in Spring 4.1.3.RELEASE
00:14:20.617 [main] INFO org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader - Skipping bean definition for [BeanMethod:name=getHelloService,declaringClass=com.foo.config.BarConfig]: a definition for bean 'helloService' already exists. This top-level bean definition is considered as an override.
Also, I have observed the same issue in Spring 4 in a completely annotation based context as well. i.e. it is not because of xml and annotation config mix but due to the Spring versions used here
Questions
What changed in Spring 5?
Is there any rule of thumb while working with a Spring application that uses both xml and annotation config especially when it comes to overriding the beans?
Also FTR, these are the solutions that worked
1.Overriding the beans using BeanPostProcessor
2.Using profiles. But this wouldn't work for complicated conditions.
#Profile("ENGLISH")
#Configuration
#Import(FooConfig.class)
public class EnglishConfig {
}
#Profile("SPANISH")
#Configuration
public class SpanishConfig {
#Bean(name="helloService")
public HelloService getHelloService() {
return new HelloService() {
#Override
public String getGreeting(String name) {
return "Hola "+name;
}
};
}
}
The issue here is that you are trying to override a xml bean from a #Configuration class, now I'm not 100% sure, but in spring 4 a xml bean still had precedence in choosing a bean, so the #Configuration beans would not get permission to overwrite the xml bean. Which was resolved in spring 5.
Your approach to use BeanPostProcessor is i guess the only viable solution for this.
I'm thinking maybe you could use a different bean name, implement your own behaviour and use #Qualifier annotation to choose which bean will get selected?

How to use "Functional bean definition Kotlin DSL" with Spring Boot and Spring WebFlux?

At https://github.com/spring-projects/spring-framework/blob/master/spring-context/src/main/kotlin/org/springframework/context/support/BeanDefinitionDsl.kt the comment shows how to define Spring Beans via the new "Functional bean definition Kotlin DSL". I also found https://github.com/sdeleuze/spring-kotlin-functional. However, this example uses just plain Spring and not Spring Boot. Any hint how to use the DSL together with Spring Boot is appreciated.
Spring Boot is based on Java Config, but should allow experimental support of user-defined functional bean declaration DSL via ApplicationContextInitializer support as described here.
In practice, you should be able to declare your beans for example in a Beans.kt file containing a beans() function.
fun beans() = beans {
// Define your bean with Kotlin DSL here
}
Then in order to make it taken in account by Boot when running main() and tests, create an ApplicationContextInitializer class as following:
class BeansInitializer : ApplicationContextInitializer<GenericApplicationContext> {
override fun initialize(context: GenericApplicationContext) =
beans().initialize(context)
}
And ultimately, declare this initializer in your application.properties file:
context.initializer.classes=com.example.BeansInitializer
You will find a full example here and can also follow this issue about dedicated Spring Boot support for functional bean registration.
Another way to do it in Spring Boot would be :
fun main(args: Array<String>) {
runApplication<DemoApplication>(*args) {
addInitializers(
beans {
// Define your bean with Kotlin DSL here
}
)
}
}
You can define your beans in *Config.kt file and implement initalize method of ApplicationContextInitializer interface.
override fun initialize(applicationContext: GenericApplicationContext) {
....
}
Some bean definition here.
bean<XServiceImpl>("xService")
bean("beanName") {
BeanConstructor(ref("refBeanName"))
}

Servlets and Filters registered as Spring Beans instead of web.xml in Grails

In the Grails 'Upgrading from 2.x to 3.0.6' document it's been noted that "new servlets and filters can be registered as Spring beans or with ServletRegistrationBean and FilterRegistrationBean respectively" however not much else is said on the matter.
I am wondering if anybody has any good input on how to do this properly (i.e., using the init/BootStrap.groovy which contains the servlet context to load beans, versus beans in conf/spring) or perhaps there is some pre-defined Spring way of doing this that's obvious and I am missing.
Note: I am trying to integrate spring-ws into Grails 3.0.6.
You should do this in doWithSpring for a plugin, or grails-app/conf/spring/resources.groovy for an app. Since Grails 3 is based on Spring Boot you can also use an #Bean method.
When the application context starts up, Spring looks for ServletRegistrationBeans, FilterRegistrationBeans, etc. and uses their configured properties to do programmatic registration in the servlet container for you.
There are some examples in the Grails source. ControllersGrailsPlugin registers some filters (e.g. here) and the main dispatcher servlet is registered here.
There's some documentation in the Spring Boot docs although it's biased towards #Bean methods, but you can use any approach to defining the beans.
(Works for Grails 3 (specifically version 3.3.3))
To add custom listener to servletContext by using "doWithSpring" section of plugin descriptor file (*GrailsPlugin.groovy):
Step #1
*GrailsPlugin.groovy
...
import org.springframework.boot.web.servlet.ServletListenerRegistrationBean
import my.custom.listeners.ContextListener
...
class MyAppGrailsPlugin extends Plugin {
...
Closure doWithSpring() {
{->
...
httpSessionServletListener(ServletListenerRegistrationBean){
listener = bean(ContextListener)
}
...
}
...
}
Step #2: Now it can be referred in e.g. service classes as:
SomeService.groovy
class SomeService{
...
def httpSessionServletListener
...
def someMethod(){
httpSessionServletListener.getSessions()
}
...
}
Step #0: Custom filter class should be written
Here is the snippet of custom listener class implementing respective interface:
ContextListener.groovy
import javax.servlet.http.HttpSession
import javax.servlet.http.HttpSessionListener
public class ContextListener implements HttpSessionListener {
...
/**
* All current sessions.
*/
public List<HttpSession> getSessions() {
synchronized (this) {
return new ArrayList<HttpSession>(sessions)
}
}
...
}

Resources