`#ConditionalOnProperty` used with multiple `#PropertySource` - spring-boot

I have some beans with #ConditionalOnProperty, where the property is taken from some #PropertySource. But I have multiple #PropertySources. Only in one of them, given property for condition will be defined. But for my surprise, the #ConditionalOnProperty annotation consults every property source. And since property isn't in every property source, PropertyResolver will flag bean as non-mathing.
What I'd like to have, is interface, with actual implementation and no-op implementation, and control which implementation will be used. I don't want to control it using profile, but profile is based on conditionals so there should be a way. So what is happening for me is, that I'm regardless of setting left out with no implementation. Sure, I can add matchIfMissing, and then I will be left out with both, regardless of setting.
Annotation is:
#ConditionalOnProperty(name = "feature.enabled", havingValue = "true")
and in property file is
feature.enabled=true
What is wrong here? But it really cannot be, that if I'm using #Conditional... I have to use just one property source, right?
UPDATE:
having following beans definitions, I have behavior as described: no bean is registered, because feature.enabled is not defined in application.properties. Adding matchIfMissing=true does not help, because bean with this parameter will be added always, since it's (always) not present in application.properties. Adding #ConditionalOnMissingBean did not help me also. I set feature.enabled to true in another.properties, but that FeatureImpl got vetoed, because there is property source, where feature.enabled isn't true. And surprisingly FeatureNoOp, at that time annotated with #ConditionalOnMissingBean, wasn't registered either. No idea why.
#Service
#Slf4j
#ConditionalOnProperty(name = "feature.enabled", havingValue = "false")
public class FeatureNoOp implements Feature {
vs
#Service
#Slf4j
#ConditionalOnProperty(name = "feature.enabled", havingValue = "true")
public class FeatureImpl implements Feature {
And configuration looks like:
#Configuration
#PropertySource("classpath:application.properties")
public class Config {
//...
#Order(Ordered.HIGHEST_PRECEDENCE)
#Configuration
#PropertySource("classpath:another.properties")
#Profile("war-deployment")
public static class WarDeploymentConfig {
//...
}
}

The true cause was, as M.Deinum correctly suggested (thanks!), in #PropertySource annotations.
I had #PropertySource for application.properties, plus I have some configuration classes like one below, just to bring another property source based on active profile:
#Configuration
#PropertySource("classpath:profile-related-properties.properties")
#Profile("some-profile")
public static class SomeProfileConfig {}
even if this provide some explicit saying what's being used, it apparently spells some issues. I changed the code, so that I do not 'repeatedly' declare usage on application.properties and renamed files/profiles so that another automatically discovered properties pattern can be used: application-{profile_name}.properties. After this change, everything works as expected.

Related

#EnableAutoConfiguration annotation with class prameter is not initializing properties object

i have following #CongfigurationProperties class
//#Component
#ConfigurationProperties
#PropertySource("classpath:typesofcharge.properties")
public class ChargeProperties {
private HashMap<String,String> charge=new HashMap<>();
public HashMap<String,String> getCharge()
{
return this.charge;
}
}
And this is my Configuration file
#SpringBootApplication
#ComponentScan({"com.vehiclemanagement.config,com.vehiclemanagement.client,"
+ "com.vehiclemanagement.controller,"
+ "com.vehiclemanagement.exception,"
+ "com.vehiclemanagement.model,"
+ "com.vehiclemanagement.service"})
#EnableConfigurationProperties(ChargeProperties.class)
public class VehicleManagementConfig {
public static void main(String[] args) {
SpringApplication.run(VehicleManagementConfig.class, args);
}
}
If i use #Component annotation in ChargeProperties and remove ChargeProperties.class annotation in Configuration class the charge HashMap is initialized properly
If i remove #Component and pass ChargeProperties.class as argument like this
#EnableConfigurationProperties(ChargeProperties.class) like how document says the charge HashMap is empty when i run
I am using spring boot 2.0.2 release .But i am following latest docs. Can anyone explain why this are not working as document suggest
content of property file is as follows
UPDATE the content of property files are as shown
#DO NOT MODIFY THIS FILE
charge.peak=Double_rate;
charge.lateNight=duration_based_charge;
charge.earlyMorning=special_offers;
When specifying ChargeProperies.class on the #EnableConfigurationProperties annotation it will be registered as a bean through the EnableConfigurationPropertiesImportSelector class inside #EnableConfigurationProperties.
So in the example, if you have only annotated the ChargeProperties class with #ConfigurationProperties it will create a chargeProperties bean with an empty charge HashMap because it defaulted back to application.properties as the source.
A custom source can be specified by using #PropertySource.
#PropertySource annotation providing a convenient and declarative mechanism for adding
a PropertySource to Spring's Environment. To be used in conjunction
with #Configuration classes.
As per documentation above, to use #PropertySource to load the custom source, one has to use the #Configuration annotation.
#Configuration
#PropertySource("classpath:typesofcharge.properties")
Under the hood a #Configuration class is a #Component.
#Target(value=TYPE)
#Retention(value=RUNTIME)
#Documented
#Component
public #interface Configuration
So to your question. By specifying a custom #PropertySource without #Configuration, spring did not load the properties in the #PropertySource annotation and defaulted back to the application.properties.
If we use #PropertySource we have to use component otherwise the properties will not be read
since we added the #ComponentScan We don't have to mention #EnableConfiguationProperties annotation at all The propety class object can be autowired as Bean

How to tie annotations to spring properties?

In my Spring boot application I have around 30 controller classes. Each class has a #CrossOrigin annotation. When I work on it in eclipse it’s fine, but when I deploy to production I need to remove them.
So I was thinking to create a custom property in application.properties and somehow tie it to CrossOrigin annotations. So I can set property my-annotation=false and this will cancel the CrossOrigin annotations everywhere in the application. I tried looking into reflection but couldn’t figure out how to do it.
How can I make this work?
I'm afraid, spring doesn't work this way - once you've put an annotation its there for all controllers.
Technically this annotation is used somewhere deep inside in spring MVC (org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping#initCorsConfiguration) so its not advisable to fiddle with it.
Probably it's possible to override the beans of this type by custom implementation and putting them into some configuration that will take place in production only and will not be loaded by default. But again, this is too "internal" solution.
Instead I suggest creating a global cors configuration outside the controller, so no #CrossOrigin annotation will stay in controllers.
So the first step would be defining a WebConfigurerAdapter:
#Configuration
class MyGlobalCorsConfiguration {
#Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurerAdapter() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/greeting").allowedOrigins("http://localhost:9000");
}
};
}
}
The second step is to apply this configuration only upon some certain conditions:
It's possible to do with #ConditionalOnProperties annotation that can be put on the whole configuration or one single bean:
#ConditionalOnProperty(value = "myproject.cors.enabled", havingValue = "true",
matchIfMissing = false)
#Configuration
class MyGlobalCorsConfiguration {
....
}
Now during the third step you should put the property myproject.cors.enabled=true into the application properties that gets loaded only in production environment or something

How to filter properties using prefix with BridgePropertyPlaceholderConfigurer in Camel

Is there a way to filter by property prefix when reading in values using #Value annotation in Camel? I'm using the BridgePropertyPlaceholderConfigurer to specify the property files and I've tried setting #ConfigurationProperties(prefix = "SlowEndpoint") on the property bean, but it seems to ignore it.
ConfigurationProperties and Value annotations were not meant to be used together in this scenario. After removing #Value annotations, the ConfigurationProperties annotation picked up properties that were named identical to the property bean members.
public class SlowEndpointConfiguration extends BaseHystrixConfigurationDefinition {
#Configuration
#Profile({"default", "local"})
#PropertySource("classpath:/properties/local/hystrix/SlowEndpointHystrix.properties")
#ConfigurationProperties(prefix = "SlowEndpoint")
static class Defaults extends SlowEndpointConfiguration{
}
}

#AutoConfigureAfter not working as desired

I have 3 spring-boot-starter projects
One of the autoconfiguration class has the following code:
#Configuration
#ConditionalOnClass(value = Config.class)
#AutoConfigureAfter(value = {FileGeneratorConfig.class, FileUploaderConfig.class})
public class JobConfig
FileGeneratorConfig and FileUploaderConfig are also autoconfiguration classes.
I was expecting that beans created in FileUploaderConfig will be created first. So test this I had put a break point in the method that creates bean in JobConfig and FileUploaderConfig. But the break point hits JobConfig first which makes me believe that my #AutoConfigureAfter is not working. Is that the right assumption.
Also in FileUploaderConfig i have this:
#Bean
FileUtilContainer fileUtilContainer(FileUtilContainerProperties fileUtilContainerProperties){
return new FileUtilContainer(FileUtil.createDirectory(fileUtilContainerProperties.getArchive()),
FileUtil.createDirectory(fileUtilContainerProperties.getWorking()),
FileUtil.createDirectory(fileUtilContainerProperties.getConfirmation()),
FileUtil.createDirectory(fileUtilContainerProperties.getConfirmationProcessed()),
FileUtil.createDirectory(fileUtilContainerProperties.getError()),
FileUtil.createDirectory(fileUtilContainerProperties.getErrorProcessed()));
}
and FileUtilContainerProperties:
#Component
#ConfigurationProperties(prefix = "batch.letter.directory", ignoreUnknownFields = false)
public class FileUtilContainerProperties
but it is not creating FileUtilContainerProperties bean. Am I missing something here?
AutoConfigureAfter controls the order in which the configuration files are processed and their bean definitions are created. The order in which beans are created from those definitions is a separate concern and depends on, among other things, the dependencies that exist between your beans.

ComponentScan.basePackageClasses vs ComponentScan.basePackages to register a single Spring webMVC Controller?

I want to add a single specific controller class to my Spring WebApplicationContext. I ran across the following example: (its in Scala, but is adapted from here: using ComponentScan or context:component-scan with only one class)
#Configuration
#ComponentScan(
basePackages = Array("com.example.controllers"),
useDefaultFilters = false,
includeFilters = Array(
new ComponentScan.Filter(`type` = FilterType.ASSIGNABLE_TYPE,
value = Array(classOf[com.example.controllers.MyController]))))
class MyConfig {
}
This works nicely (but is very verbose).
But Spring's #ComponentScan also has basePackageClasses
#Configuration
#ComponentScan( basePackageClasses=Array(classOf[com.example.controllers.MyController]))
class MyConfig {
}
Of basePackageClasses, Spring's docs says:
Type-safe alternative to basePackages() for specifying the packages to
scan for annotated components.
However, while the first ComponentScan correctly adds only com.example.controllers.MyController, but the second cause all of my #Controller to be scanned and added! Why? What's the use of basePackageClasses?
Example like: https://github.com/mikaelhg/springmvc-example/blob/master/src/main/java/mikaelhg/example/ExampleConfiguration.java
suggest that basePackageClasses can be used to load a single component.
Update:
As an aside, replacing:
#Configuration
#ComponentScan(
basePackages = Array("com.example.controllers"),
useDefaultFilters = false,
includeFilters = Array(
new ComponentScan.Filter(`type` = FilterType.ASSIGNABLE_TYPE,
value = Array(classOf[com.example.controllers.MyController]))))
class MyConfig {
}
with
#Configuration
class MyConfig {
#Bean
var myController = new com.example.controllers.MyController
}
it seems that MyController never gets gets connected to the servlet (the behavior changed to 404-NotFound) -- when added to a WebApplicationContext.
The full javadoc for that attribute reads
Type-safe alternative to basePackages() for specifying the packages to
scan for annotated components. The package of each class specified
will be scanned.
Consider creating a special no-op marker class or interface in each
package that serves no purpose other than being referenced by this
attribute.
By type-safe, you can't make any mistakes with the String value of the name of the package. If you specify an incorrect class, it will fail at compile time.
When you specify basePackageClasses, Spring will scan the package (and subpackages) of the classes you specify. This is a nice trick with no-op classes/interfaces like Controllers, Services, etc. Put all your controllers in one package containing the Controllers class and specify your Controllers class in the basePackageClasses. Spring will pick them all up.
You still need to specify the filters.

Resources