I recently implemented a Spring Cloud Config Server. My application (client) is able to retrieve application profiles and refresh them in flight.
Unfortunately, CORS settings, which are also declared there, are not reloaded by Spring during runtime. I did a small investigation and check that in terms of refresh everything looks good - ConfigurationPropertiesRebinder rebind() method successfully destroys old bean holding CORS setup and creates a new with up-to-date settings. I also see that Environment bean is holding new settings in propertySources field.
Is there any way to force Spring to reload CORS during runtime or should I need to reload application context?
Use below code for resolve CORS issue.
#Configuration
#EnableWebMvc
class WebConfig implements WebMvcConfigurer {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**");
}
}
Related
I have a Spring Boot server that I would like to be able to communicate with websocket clients that are unable or unwilling to handle permessage-deflate compressed messages. I know from both of these similar questions on the topic (linked below) that I can add the VM argument -Dorg.apache.tomcat.websocket.DISABLE_BUILTIN_EXTENSIONS=true to disable Tomcat's default deflate compression.
Tomcat JSR356 Websocket - disable permessage-deflate compression
Spring disable websocket compression -> Sec-WebSocket-Extensions: permessage-deflate
However, I plan to make a program that will be distributed so others can run it, and having to force people to remember to always include a specific VM argument just to change a setting seems quite abrasive.
Is there some alternative way to disable Tomcat's websocket compression which doesn't require users to specify VM arguments at runtime, perhaps using Spring's Java configuration or a custom websocket handshake interceptor?
You can not only set properties using JVM arguments but also programmatically using System.setProperty like the following:
System.setProperty("org.apache.tomcat.websocket.DISABLE_BUILTIN_EXTENSIONS",String.valueOf(true));
If you export your project to a JAR-file using embedded tomcat, you can run it in your main before executing SpringApplication.run:
public static void main(String[] args) {
System.setProperty("org.apache.tomcat.websocket.DISABLE_BUILTIN_EXTENSIONS",String.valueOf(true));
SpringApplication.run(YourApplicationClass.class,args);
}
If you package your application to a WAR file, you could try the following:
#SpringBootApplication
public class YourApplicationClass extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
System.setProperty("org.apache.tomcat.websocket.DISABLE_BUILTIN_EXTENSIONS",String.valueOf(true));
return application.sources(YourApplicationClass.class);
}
}
I'm currently using an EnvironmentPostProcessor to add my external PropertySource, the code looks like this :
public class ExternalPropertySourceEnvironmentPostProcessor implements EnvironmentPostProcessor
{
private static final String EXTERNAL_PROPERTY_SOURCE_NAME = "ExternalPropertySource";
#Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application)
{
if (environment.acceptsProfiles(Profiles.EXTERNAL_PROPERTY_SOURCE_ENABLED_PROFILE)) {
environment.getPropertySources()
.addLast(new ExternalPropertySource(EXTERNAL_PROPERTY_SOURCE_NAME, new ExternalSource()));
}
}
}
A spring.factories is also used to register this EnvironmentPostProcessor.
This code actually works if the profile is set in the vm environment variables, but if it is added in src/main/resources/application.yml, the profile doesn't seem to be injected yet in the environment and is not returned by the environment.getActiveProfiles(). I've tried using the interface Ordered with the lowest precedence, but it doesn't help.
To add a bit of context around what I'm trying to achieve, this class is in a small library that adds an external property source like a database. Then we can use it in some other spring boot applications. Something like Spring Cloud Config does.
I'd like a clean way to enable or disable this property source depending on the environment where the code runs. I don't mind using something else then profiles or even another way to inject the property source, I just want something clean that doesn't depend on several factors to work.
The main problem in my code right now is that I'm using spring boot's property sources to make my own property source configurable.
UPDATE : I used a Spring Cloud app to debug this, and was confusing the bootstrap context with the normal spring boot context. See my answer below.
Further investigation made me figure out the problem appeared only with a Spring Cloud application.
In fact the breakpoint I had in this code was triggered twice, once after the bootstrap context initialization and once after the spring boot context initialization. I was only debugging the profiles in the first occurence. At that point, only the bootstrap.yml properties are loaded and not the ones from the application.yml file. The second occurence obviously had the profiles from my application.yml file.
My code worked as expected with a vanilla Spring Boot application. As the documentation states :
The Environment has already been prepared with all the usual property
sources that Spring Boot loads by default.
I was confused by the behaviour of my app which seemed to be different from that statement, but it was Spring Cloud's bootstrap that was messing with my debugging.
Since I need a PropertySource that has the highest precedence, I need to add it in the post bootstrap initialization for Spring Cloud apps. I used an init flag on my EnvironmentPostProcessor so it doesn't get executed twice and used the bootstrap.yml on Spring Cloud apps to set the profile.
TL;DR :
With Spring Cloud, an EnvironmentPostProcessor gets called twice: once after the bootstrap init and once after the normal Spring Boot context init. If you need injected properties and are targeting the Spring Cloud's post bootstrap initialization, use the bootstrap.yml instead of application.yml.
I want my spring boot server to be up even if my redis cache isnt up.
I am using #EnableCachingannotation , but my server startup fails if redis is down giving me BeanCreationException. Because BeanCreationException cannot be handled , the only option I have is to lazy load my #EnableCaching class.
I tried annotating that class as under:
#Configuration
#EnableCaching
#Lazy
#Profile("dev")
public class RedisCache extends CachingConfigurerSupport {}
But still this bean is getting loaded at server startup and startup therefore fails.
How do I lazy load the above mentioned class
As far as I know Spring configuration is alwasy loaded on application startup, because that is when the ApplicationContext is created.
In order to do what you want you'd either have to create some sort of custom implementation of ApplicationContext (although I honestly cannot think of how it should work regarding the dependency resolution for dependency injection) or create custom wrapper for caching which would not try to establish connection to Redis until the cache is used.
It may also be possible to configure Spring Boot to skip this particular #Configuration class (Using Boot configuration classes) and then to manually create AnnotationConfigApplicationContext and then retrieve Redis connection Beans from this context manually rather than autowiring them.
I'd trying to activate basic authentication with password hash encryption.
#Configuration //gets picked up automatically by spring-boot
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(details).passwordEncoder(new BCryptPasswordEncoder());
}
}
I want the authentication only being active in production. So I'm trying to deactivate it in general at first, use:
security.basic.enabled=false
Result: the application is still secured. I'm presented with the username/password screen.
But how could I then disable the need for authentication?
Solved with ConditionalOnProperty:
#Configuration
#ConditionalOnProperty("security.basic.enabled")
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
}
To do different things in different environments you should use Spring Profiles
Suppose you need security configuration bean only in production environment, then you should mark it to be loaded conditionally when a specific profile is enabled.
#Configuration //gets picked up automatically by spring-boot
#Profile("Production")
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(details).passwordEncoder(new BCryptPasswordEncoder());
}
}
Profile specific configuration is done in properties files named application-{profile}.properties and bootstrap-{profile}.properties files.
Now since security configuration bean is marked with profile Production it will be loaded only when Production profile is enabled.
To enable a profile you have to set following property.
#One or more profiles can be active simultaneously
spring.profiles.active=Production,Dev,Local
This property can either be edited in the common properties file application.properties (Always loaded unlike profile specific properties files like application-Production.properties which are conditionally loaded) or it can provided as a environment variable as below.
On Windows
set SPRING_PROFILES_ACTIVE=Production
On Unix
export SPRING_PROFILES_ACTIVE=Production
It can be done using a million other ways that spring supports to load properties. For a list of all the methods go through this link https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html
Finally to disable default Spring security (Basic) Autoconfiguration, you can use following properties.
security.basic.enabled=false
management.security.enabled=false #For actuator
Above should be in the profile specific properties files where you wish to disable Spring Security auto configuration.
To disable spring-security autoconfiguration entirely use the following property
spring.autoconfigure.exclude[0]=org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration
What could be the problem if Spring Boot cannot resolve view with specific name?
I have developed many Spring Boot applications before and have never encountered this problem.
I have following folder structure:
/src/main/java
/src/main/resources/config
/src/main/resources/static
/src/main/resources/static/partials
/src/main/resources/static/resources
Image:
/src/main/resources/static/resources folder is not making problems for having resources name. I tried renaming it and it didn't solve problem.
I am using Maven and I have parent project which has multiple children projects.
Module for the rest api contains many configuration classes, including configurations for security.
Everything is working as it should, application can be started without errors, but when I tried to add static content I encountered this problem.
From the following example (example) we can see that Spring Boot should automatically maps src/main/resources/static/index.html to the root path, but that is not happening in my application. From the log I can see that Spring didn't find mapping for /.
I am able to run that example normally, but in my application it is not working.
Because Spring didn't map automatically I created WelcomeController which should return view:
#Controller
public class WelcomeRestController {
#RequestMapping("/")
public String welcome() {
return "index.html";
}
}
After I did that, mapping is found, but Spring reports that it couldn't resolve view with name index.html even if it exists.
I tried adding static content to src/main/webapp/static folder even if it is not recommended and it also didn't work, same problem, cannot resolve view.
I also tried to configure using
#Configuration
public class WebMvcConfiguration extends WebMvcConfigurerAdapter {
#Override public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("index.html");
}
}
Again, view cannot be resolved.
My application's start point:
#Import(MyRestServiceConfiguration.class)
#SpringBootApplication
public class Application extends SpringBootServletInitializer {
public static void main(final String[] args) {
SpringApplication.run(Application.class, args);
}
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(Application.class);
}
}
I am using embedded Tomcat.
Is it possible that security configurations or some other configurations except WebMvcConfigurerAdapter are having influence on views?
I am trying to resolve this issue for hours but I can't find soulution. Any help would be appreciated.
EDIT:
I am not building application using Maven, just running Application.java as Java Application, so Maven should not be a problem.
EDIT:
Content of index.html:
<!DOCTYPE html>
<html >
<head lang="en">
</head>
<body>
<div>Hello</div>
</body>
</html>
I have found a solution, but I am not really sure why this makes problem. Please post your answer if you know why is this happening.
The problem was #EnableWebMvc annotation in MyRestServiceConfiguration
#Configuration
// #EnableWebMvc
#Import(MyServiceConfiguration.class)
public class MyRestServiceConfiguration {
}
After removing it, everything works as it should.
From Spring Boot guide:
Normally you would add #EnableWebMvc for a Spring MVC app, but Spring Boot adds it automatically when it sees spring-webmvc on the classpath. This flags the application as a web application and activates key behaviors such as setting up a DispatcherServlet.
It's clear that #EnableWebMvc annotation is not needed, but why does it make problems?
EDIT:
From Spring Boot reference:
The easiest way to take complete control over MVC configuration is to provide your own #Configuration with the #EnableWebMvc annotation. This will leave all MVC configuration in your hands.
From Spring's #EnableWebMvc documentation:
Adding this annotation to an #Configuration class imports the Spring MVC configuration from WebMvcConfigurationSupport, e.g.:
#Configuration
#EnableWebMvc
#ComponentScan(basePackageClasses = { MyConfiguration.class })
public class MyWebConfiguration {
}
So, the problem was that #EnableWebMvc annotation dissabled default Spring Boot's configuration and because of that Spring didn't know where to look for static resources. If I got something wrong, please correct me.