Loading String[] from properties file into origins field of #CrossOrigin using property placeholder expression - spring

In my spring boot application I have the following controller
#RestController(value = "ProjectController")
#CrossOrigin(origins = {"${app.api.settings.cross-origin.urls}"})
public class ProjectController {
// some request mapping methods
}
The property app.api.settings.cross-origin.urls is already a key having comma separated valid urls in application.properties file like
app.api.settings.cross-origin.urls=http://localhost:3000, http://localhost:7070
This approach works till I have only single value like
app.api.settings.cross-origin.urls=http://localhost:3000 but not for comma separated values.
The origins field inside #CrossOrigin is of type String[] still it does not convert into String[] automatically.
I mean there should be some way to achieve this provided by the framework. Not a work around.
I can achieve using comma separated urls from properties files using #Value into a List<String> or String[] as a field inside a #Configuration class like below
#Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
#Value("${app.api.settings.cross-origin.urls}")
private String[] consumerUiOrigins;
#Override
public void addCorsMappings(CorsRegistry registry) {
registry
.addMapping("/api/**")
.allowedOrigins(consumerUiOrigins);
}
}
But this would be a global configuration having application wide applicability. I want to stick to the more fine grained #CrossOrigin annoation based CORS configuration.
So I put my question clearly below.
Is it possible to inject comma separated value from properties file as String[] using property plcaholer expression (${*}) into spring annotation fields having the same type i.e. String[] ????? If yes then how??? If no then can we tweak some core framework classes to achieve this??? Anyone please help....
P.S. - Please do not mark my question as duplicate of Use Spring Properties in Java with CrossOrigin Annotation or in Spring-Config XML
My question is more on usage of property placholder expressions inside spring annotation fields having multi element type like String[] and less on the configuration of CORS in spring applications.

Try doing as below in application.properties:
app.api.settings.cross-origin.urls="http://localhost:3000","http://localhost:7070"

Related

Spring How to map list of custom pojo

In my application.yml file, I want to define a list of rules.
rules:
- name: abc
value: something
- name: edf
value: something
Then I want to define a service like this
#Service
public class MyService {
public MyService(#Value("${rules}") List<Rule> rules) {
}
}
For the Rule pojo, it's like this.
public class Rule {
public String name, value;
}
Currently, I'm facing these errors.
If I leave rules empty, it throws can't convert String to List<Rule>
rules: []
If I keep the values, it throws could not resolve placeholder 'rules'
I really don't know what I'm doing wrong here.
From Spring docs, I found this.
Using the #Value("${property}") annotation to inject configuration
properties can sometimes be cumbersome, especially if you are working
with multiple properties or your data is hierarchical in nature.
Spring Boot provides an alternative method of working with properties
that lets strongly typed beans govern and validate the configuration
of your application
Link: https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html#boot-features-external-config-typesafe-configuration-properties
At the end, I have to introduce another class.
#Configuration
#ConfigurationProperties(prefix="rules")
public class Rules {
public List<Rule> list;
}
Then I autowire it in MyService.

how to load property file in to spring boot project with annotations?

I have written queries in property file. I want to read the property file in to one class with annotations in spring boot. How can i read it? And is there any better approach for writing queries in spring boot project?
If you add your properties in application.properties file, you can read them inside the spring boot classes like:
#Service
public class TwitterService {
private final String consumerKey;
private final String consumerKeySecret;
#Autowired
public TwitterService(#Value("${spring.social.twitter.appId}") String consumerKey, #Value("${spring.social.twitter.appSecret}") String consumerKeySecret) {
this.consumerKey = consumerKey;
this.consumerKeySecret = consumerKeySecret;
} ...
You can annotate fields in your components by #Value("${property.name}")
Else, you can use Properties Object in java.util package.
For example, i have a mode property, which values are dev or prod, i can use it in my beans as follow :
#Value("${mode:dev}")
private String mode;
The other approach is by using :
Properties pro = new Properties();
pro.load(this.getClass().getClassLoader().getResourceAsStream());
You can use #PropertySource to read the properties from a file and then pass them to a bean. If you have a file called "queries.properties" that has a property like:
query1: select 1 from foo
Then your config might look like:
#PropertySource("classpath:queries.properties")
#Configuration
public class MyConfig {
#Bean
public DbBean dbBean(#Value("${queries.query1}") String query) {
return new DbBean(query);
}
}

Swagger2Markup : how to group by tags when using Swagger remote endpoint from test?

I'm using the great swagger2markup plugin to generate Asciidoc documentation for my REST API as provided by Swagger. I have followed swagger2markup documentation and I am using a Spring MVC integration test to generate markup from my Springfox Swagger endpoint like this (I'm using Maven) :
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(classes = { AppConfig.class, SwaggerConfig.class })
public class DocumentationIT {
protected MockMvc mockMvc;
#Autowired
protected WebApplicationContext webApplicationContext;
#Rule
public JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation("src/docs/asciidoc/apidoc/generated-snippets");
#Before
public void setUp(){
this.mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext)
.apply(documentationConfiguration(this.restDocumentation))
.build();
}
#Test
public void convertSwaggerToAsciiDoc() throws Exception {
this.mockMvc.perform(get("/v2/api-docs")
.accept(MediaType.APPLICATION_JSON))
.andDo(
Swagger2MarkupResultHandler
.outputDirectory("src/docs/asciidoc/apidoc")
.withExamples("src/docs/asciidoc/apidoc/generated-snippets").build())
.andExpect(status().isOk());
}
}
Everything is working great and all my paths are in my final documentation, however paths all appear directly at the root and are not grouped by resources (i.e. by controllers), so Method 1 from Controller 1 will appear at the same level as Method 2 from Controller 2.
My output :
What I'd like :
From what I can see, when using generation from a local file like in this swagger2-markup Maven project template you can specify a property to tell swagger2markup to group your paths by tags using the config property <swagger2markup.pathsGroupedBy>TAGS</swagger2markup.pathsGroupedBy>, however there does not seem to be such configuration when using Swagger2MarkupResultHandler from a test. The only option is withMarkupLanguage() but there is no withPathsGroupedBy() method...
Am i missing something here ?
As you mentioned, there is a property of swagger2markup.pathsGroupedBy provided by swagger2Markup to specify how the paths should be grouped. However, Swagger2MarkupResultHandler do not provide API to support the configuration.
According to Swagger2Markup API,
The properties of Swagger2Markup are defined in the class
io.github.swagger2markup.Swagger2MarkupProperties. The properties are
considered in the following order:
Java System properties
Custom properties
Default properties (included in Swagger2Markup)
it is possible to configure it by using system properties. Therefore, you can set a system property of swagger2markup.pathsGroupedBy to TAGS in the test.
If you prefer to configure it with Java API, you may extend Swagger2MarkupResultHandler and override the handle method with the use of Swagger2MarkupConfigBuilder API.

Prefix properties of multiple PropertyPlaceholderConfigurers

I want to use Spring's PropertyPlaceholderConfigurer to read two property files. I can load each of them by using one of the following tags:
<context:property-placeholder location="class path:com/myapp/internal.properties"/>
<context:property-placeholder location="file://${settings.location}/external.properties"/>
I am not allowed to change the keys in those two files. Both files may contain entries with the same key.
I need to inject the value of a specific file.
//Pseudocode of injecting a property of a specific file
#Value("${internal.properties:my.key}")
String internalValue;
#Value("${external.properties:my.key}")
String externalValue;
So how to specify the file, and not only the key?
you will have to translate it to xml if needed:
public class InternalPropertyPlaceholderConfigurer extends PropertySourcesPlaceholderConfigurer{
public UploaderPropertyPlaceholderConfigurer() {
setLocations(new ClassPathResource[]{
new ClassPathResource("com/myapp/internal.properties"),
});
setPlaceholderPrefix("$internal{");
setPlaceholderSuffix("}");
}
and register it in spring ( or use #Component in above class ):
#Bean
public InternalPropertyPlaceholderConfigurer propertyPlaceholderConfigurer() {
return new InternalPropertyPlaceholderConfigurer();
}
this way you should be able to inject properties with this rather ugly syntax:
#Value("$internal{your.key}")
private String value;
If it works, then just add 2nd bean for external :)

Spring MVC: Ambiguous RequestMappings after moving to JavaConfig

After moving from XML setup to a JavaConfig setup many of our RequestMappings have broken and now return ambiguous method errors. Our methods rely on #PathVariable's with regular expressions to determine which to call. For example:
#RequestMapping(value={"/{id:\\d+}/boats"})
public String getBoatsById(#PathVariable("id") Long id, Model model,
HttpServletRequest request) throws Exception {...}
#RequestMapping(value={"/{id}/boats"})
public String getBoatsByName(#PathVariable("id") String id, Model model,
HttpServletRequest request) throws Exception {...}
This use to work with out issue but using the new JavaConfig setup versus the XML setup it breaks with the ambiguous errors due to the mappings.
The JavaConfig class starts as such:
#Configuration
#EnableWebMvc
#ComponentScan(basePackages = "com.example", excludeFilters = { #ComponentScan.Filter( Configuration.class ) })
public class WebConfig extends WebMvcConfigurationSupport
Would it have anything to do with the XML setup using the AnnotationMethodHandlerAdapter versus the JavaConfig class now using the recommended RequestMappingHandlerAdapter? Is there a setting I am missing?
Just browsed the source code, AnnotationMethodHandlerAdapter uses a different RequestMappingInfo than RequestMappingHandlerAdapter. The former ignores the pattern/path you have specified when checking for equality while the latter honors it. That's why you are having ambiguous mapping errors. Not sure if it is a bug or not, probably it's good to ask the people at the spring-contrib mailing list.
EDIT
Probably it's a good idea to change your mappings. The {name:reg_exp} syntax was introduced not to solve ambiguity. Excerpt from the official documentation:
Sometimes you need more precision in defining URI template variables.
Consider the URL "/spring-web/spring-web-3.0.5.jar". How do you break
it down into multiple parts?
The #RequestMapping annotation supports the use of regular expressions
in URI template variables. The syntax is {varName:regex} where the
first part defines the variable name and the second - the regular
expression.For example:
#RequestMapping("/spring-web/{symbolicName:[a-z-]+}-{version:\d\.\d\.\d}{extension:\.[a-z]+}")
public void handle(#PathVariable String version, #PathVariable String extension) {
// ...
}
}
Your handler methods were ambiguous from the start, you only provided a hackish way to solve the ambiguity.

Resources