SAMLBootstrap bean wipes out #Value injection from Cloud Config Server - spring

When I run my app, metadataFile is always null when the bean below is created. I have a Spring Cloud Config Server which I see is hit before my breakpoint and the YML is successfully retrieved.
Even if the Config Server was down, the SPEL should provide a default. Has anyone run into #Value not evaluating and injecting values before their #Bean? I have many years using XML annotations, so perhaps I never have hit this with Annotation driven config, but it seems hard to believe I would not have run into this before. Very confused...
Within it:
#Configuration
public class Test {
#Value("${someplace.saml.idp.metadata.file:'classpath:idp-metadata.xml'}")
String metadataFile;
#Bean
MetadataProvider metadataProvider() {
if(!StringUtils.isBlank(metadataFile) && metadataFile.startsWith("classpath:/")) {
// do some stuff
} else {
File metadatFile = new File(metadataFile);
}
}
UPDATE:
I shortened my example above for sake of brevity. The culprit wiping out the configuration values is this SAMLBootstrap bean. It seems to be required for annotation-configured Spring SAML2.
#Bean
SAMLBootstrap samlBootstrap() {
return new SAMLBootstrap();
}

UPDATE 2 (THE SOLUTION):
The SAMLBootstrap bean needs to be declared with static. I found this in the last comment on another post: Spring Bean without id or name in Java Config

Related

Change value at Runtime in Spring Boot

I have the below code .Can I change the ThreadPoolExecutor thread number size at run time ?
I am using spring boot.
#Configuration
public class ExecutorConfig
{
#Value(numberOfThreads)
private String numberOfThreads ; // numberOfThreads is configured app.properties file
#Bean
public ThreadPoolExecutor executorConfig()
{
ThreadPoolExecutor e = Executors.newFixedThreadPool(numberOfThreads);
return e;
}
}
One option is to add a set method for the property numberOfThread and then provide a way to update it, like a new endpoint. But if your app restarts it will still get the previous value from application.properties.
Other option is to use Spring Cloud Config, but this may or may not be overkill for your case.
Also, this answer goes a bit deeper with some code examples to force a reload.

Load custom properties file in Spring Boot MVC Main

I have created a myApp.properties in resources folder location and mentioned the server.port in this file.
myApp.properties
myApp.server.port=8020
Now I want to read load this property into my application. But I have to read this before I actually a server.
Here I am trying to do like this
#SpringBootApplication
#ComponentScan(basePackages = {"com.myorg.myapp" })
#EnableConfigurationProperties
#PropertySource("classpath:myApp.properties")
#Component
public class MyAppApplication {
#Value("${myApp.server.port}")
private static String serverPort;
public static void main(String[] args) throws Exception{
try {
SpringApplication appCtxt = new SpringApplication(MyAppApplication.class);
appCtxt.setDefaultProperties(Collections
.singletonMap("server.port", serverPort));
appCtxt.run(args);
} catch (Exception e) {
e.printStackTrace();
}
}
But serverPort is coming as null.
I also tried to create a separate Config file like this but it can't be accessed in static main
#Configuration
#PropertySource("myApp.properties")
#ConfigurationProperties
public class MyAppConfig {
#Value("${myApp.server.port}")
private String serverPort;
/**
* #return the serverPort
*/
public String getServerPort() {
return serverPort;
}
}
Any suggestion would be helpful.
Spring boot injects properties during the initialization of the application context.
This happens (gets triggered) in the line:
appCtxt.run(args);
But you try to access the property before this line - that why it doesn't work.
So bottom line, using "#Value" in the main method doesn't work and it shouldn't.
Now from the code snippet, it looks like you could merely follow the "standards" of spring boot and create the file application.properties with:
server.port=1234
The process of starting the embedded web server in spring boot honors this property and bottom line it will have the same effect and Tomcat will be started on port 1234
Update 1
Based on OP's comment:
So, how can I have multiple application.properties.
In the Spring Boot's documentation it is written that application.properties are resolved from the classpath. So you can try the following assuming you have different modules A,B,C and web app D:
Create src/main/resources/application.properties inside each of 4 modules and pack everything together. The configuration values will be merged (hopefully they won't clash)
If you insist on naming properties A.properties, B.properties and C.properties for each of non-web modules, you can do the following (I'll show for module A, but B and C can do the same).
#Configuration
#PropertySource("classpath:A.properties")
public class AConfiguration {
}
Create in Module A: src/main/resources/A.properties
If you need to load the AConfiguration automatically - make the module A starter (using autoconfig feature of spring-boot):
Create src/resources/META-INF/spring.factories file with the following content:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
<package_of_AConfiguration>.AConfiguration
Also this has been the requirement to separate C from entire bundle where it might run as bundle for some and as a separate for some others
Although I haven't totally understood the requirement, but you can use #ConditionalOnProperty for configuration CConfiguration (that will be created just like AConfiguration.java in my previous example) but this times for module C.
If the conditional is met, configuration will run and load some beans / load its own properties or whatever. All in all conditionals (and in particular Profiles in spring) can help to reach the desired flexibility.
By default, the application.properties file can be used to store property pairs, though you can also define any number of additional property files.
If you save myApp.server.port=8020 in application.properties, it will work fine.
To register a custome property file, you can annotate a #Configuration class with the additional #PropertySource annotation:
#Configuration
#PropertySource("classpath:custom.properties")
#PropertySource("classpath:another.properties")
public class ConfigClass {
// Configuration
}
make sure, your class path is correct.

how to load config in spring-webflux without spring-boot?

I just take some experiments with spring webflux 5.0.0 and Kotlin, and I have problem with loading configuration from application.yml
For base project I start with this example spring-kotlin-functional
But there are only manual loading beans and routing without any loading from configuration files or example how to implement analog of #ConfigurationProperties class in such way.
I have try to take environment in beans section:
data class DbConfig(
var url: String = "",
var user: String = "",
var password: String = ""
)
fun beans(): BeanDefinitionDsl = beans {
bean {
//try to load config from path=db to data class DbConfig
env.getProperty("db", DbConfig::class.java)
}
bean<DBConfiguration>()
//controllers
bean { StatsController(ref()) }
bean { UserController(ref()) }
//repository
bean { UserRepository(ref()) }
//services
bean { StatsService(ref()) }
//routes
bean { Routes(ref(), ref()) }
bean("webHandler") {
RouterFunctions.toWebHandler(ref<Routes>().router(), HandlerStrategies.builder().viewResolver(ref()).build())
}
//view resolver
bean {
val prefix = "classpath:/templates/"
val suffix = ".mustache"
val loader = MustacheResourceTemplateLoader(prefix, suffix)
MustacheViewResolver(Mustache.compiler().withLoader(loader)).apply {
setPrefix(prefix)
setSuffix(suffix)
}
}
}
but there are only system properties in Environment
So the question is how to load configuration from application.yml and how to implement analog of #ConfigurationProperties in such functional style?
And do I understand correctly that without spring-boot all annotations (like #Bean, #Repository, #Transactional and other) will not work for Beans?
My sources: github
Update 2017-10-21
Find a solution. The problem was related to the fact that there there were no any BeanPostProcessor. And after I include this two processors:
bean<CommonAnnotationBeanPostProcessor>()
bean<ConfigurationClassPostProcessor>()
annotations #Configuration,#Bean and #PostConstruct start to work. But annotation #ConfigurationProperties exists only in spring-boot dependency, and yml parsing classes I find only in spring-boot-starter..
After including dependency spring-boot-starter and adding bean<ConfigurationPropertiesBindingPostProcessor>() to beans section, annotation #ConfigurationProperties start to work, but config from application.yml was also not included. So I add this section:
val resource = ClassPathResource("/application.yml")
val sourceLoader = YamlPropertySourceLoader()
val properties = sourceLoader.load("main config", resource, null)
environment.propertySources.addFirst(properties)
to GenericApplicationContext configuration. And now all work as I expect, but with including a dependency spring-boot-starter.
Full code sample: version with fixes
Spring boot is just a dependency management that build auto-configuration that you can override.
All the feature are inherited from Spring framework and modules. So basically you could do the same with or without boot.
I'm not on webflux yet. But as you reference your other beans, you mat need to declare a configuration bean elsewhere.
I'm sure I'll help you with that...

java.lang.NullPointerException in Spring Boot [duplicate]

This question already has answers here:
Why is my Spring #Autowired field null?
(21 answers)
Closed 6 years ago.
I was able to use RestTemplate and autowire it. However I want to move my rest template related part of code into another class as follows:
public class Bridge {
private final String BASE_URL = "http://localhost:8080/u";
#Autowired
RestTemplate restTemplate;
public void addW() {
Map<String, String> x = new HashMap<String, String>();
W c = restTemplate.getForObject(BASE_URL + "/device/yeni", W.class, x);
System.out.println("Here!");
}
}
And at another class I call it:
...
Bridge wb = new Bridge();
wb.addW();
...
I am new to Spring and Dependency Injection terms. My restTemplate variable is null and throws an exception. What can I do it how to solve it(I don't know is it related to I use new keyword)?
Using Bridge wb = new Bridge() does not work with dependency injection. Your restTemplate is not injected, because wb in not managed by Spring.
You have to make your Bridge a Spring bean itself, e.g. by annotation:
#Service
public class Bridge {
// ...
}
or by bean declaration:
<bean id="bridge" class="Bridge"/>
Just to add further to Jeha's correct answer.
Currently, by doing
Bridge wb = new Bridge();
Means that, that object instance is not "Spring Managed" - I.e. Spring does not know anything about it. So how can it inject a dependency it knows nothing about.
So as Jeha said. Add the #Service annotation or specify it in your application context xml config file (Or if you are using Spring 3 you #Configuration object)
Then when the Spring context starts up, there will be a Singleton (default behavior) instance of the Bridge.class in the BeanFactory. Either inject that into your other Spring-Managed objects, or pull it out manually e.g.
Bridge wb = (Bridge) applicationContext.getBean("bridge"); // Name comes from the default of the class
Now it will have the dependencies wired in.
If you want to use new operator and still all dependency injected, then rather than making this a spring component (by annotating this with #Service), make it a #Configurable class.
This way even object is instantiated by new operator dependencies will be injected.
Few configuration is also required. A detailed explanation and sample project is here.
http://spring-framework-interoperability.blogspot.in/2012/07/spring-managed-components.html

Reloading/Refreshing Spring configuration file without restarting the servlet container

How can I refresh Spring configuration file without restarting my servlet container?
I am looking for a solution other than JRebel.
For those stumbling on this more recently -- the current and modern way to solve this problem is to use Spring Boot's Cloud Config.
Just add the #RefreshScope annotation on your refreshable beans and #EnableConfigServer on your main/configuration.
So, for example, this Controller class:
#RefreshScope
#RestController
class MessageRestController {
#Value("${message}")
private String message;
#RequestMapping("/message")
String getMessage() {
return this.message;
}
}
Will return the new value of your message String property for the /message endpoint when refresh is invoked on Spring Boot Actuator (via HTTP endpoint or JMX).
See the official Spring Guide for Centralized Configuration example for more implementation details.
Well, it can be useful to perform such a context reload while testing your app.
You can try the refresh method of one of the AbstractRefreshableApplicationContext class: it won't refresh your previously instanciated beans, but next call on the context will return refreshed beans.
import java.io.File;
import java.io.IOException;
import org.apache.commons.io.FileUtils;
import org.springframework.context.support.FileSystemXmlApplicationContext;
public class ReloadSpringContext {
final static String header = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
"<!DOCTYPE beans PUBLIC \"-//SPRING//DTD BEAN//EN\"\n" +
" \t\"http://www.springframework.org/dtd/spring-beans.dtd\">\n";
final static String contextA =
"<beans><bean id=\"test\" class=\"java.lang.String\">\n" +
"\t\t<constructor-arg value=\"fromContextA\"/>\n" +
"</bean></beans>";
final static String contextB =
"<beans><bean id=\"test\" class=\"java.lang.String\">\n" +
"\t\t<constructor-arg value=\"fromContextB\"/>\n" +
"</bean></beans>";
public static void main(String[] args) throws IOException {
//create a single context file
final File contextFile = File.createTempFile("testSpringContext", ".xml");
//write the first context into it
FileUtils.writeStringToFile(contextFile, header + contextA);
//create a spring context
FileSystemXmlApplicationContext context = new FileSystemXmlApplicationContext(
new String[]{contextFile.getPath()}
);
//echo the bean 'test' on stdout
System.out.println(context.getBean("test"));
//write the second context into it
FileUtils.writeStringToFile(contextFile, header + contextB);
//refresh the context
context.refresh();
//echo the bean 'test' on stdout
System.out.println(context.getBean("test"));
}
}
And you get this result
fromContextA
fromContextB
Another way to achieve this (and maybe a more simple one) is to use the Refreshable Bean feature of Spring 2.5+
With dynamic language (groovy, etc) and spring you can even change your bean behavior. Have a look to the spring reference for dynamic language:
24.3.1.2. Refreshable beans
One of the (if not the) most
compelling value adds of the dynamic
language support in Spring is the
'refreshable bean' feature.
A refreshable bean is a
dynamic-language-backed bean that with
a small amount of configuration, a
dynamic-language-backed bean can
monitor changes in its underlying
source file resource, and then reload
itself when the dynamic language
source file is changed (for example
when a developer edits and saves
changes to the file on the
filesystem).
I wouldn't recommend you to do that.
What do you expect to happen to singleton beans which their configuration modified? do you expect all singletons to reload? but some objects may hold references to that singletons.
See this post as well Automatic configuration reinitialization in Spring
You can take a look at this http://www.wuenschenswert.net/wunschdenken/archives/138 where once you change any thing in the properties file and save it the beans will be reloaded with the new values.

Resources