Spring - YML array property is null - java

I am vainly trying to read an array of strings from the application.yml.
Both the Environment and the #Value annotation, always return null.
Everything works if I read an item, instead of the entire array.
Here the code:
Sources
Boot Application and Rest Controllers
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.core.env.Environment;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
#RestController
class WithEnvCtrl {
#Autowired
private Environment env;
#RequestMapping(value = "/with_env", method = { RequestMethod.GET, RequestMethod.POST }, produces = "application/json")
public String test() {
System.err.println(env.getProperty("this.is.array[0]"));
System.err.println(env.getProperty("this.is.array", List.class));
System.err.println(env.getProperty("this.is.array", String[].class));
return env.getProperty("this.is.array[0]");
}
}
#RestController
class WithValueAnnotation {
#Value("${this.is.array[0]}")
private String first;
#Value("${this.is.array}")
private List<String> list;
#Value("${this.is.array}")
private String[] array;
#RequestMapping(value = "/with_value_annotation", method = { RequestMethod.GET, RequestMethod.POST }, produces = "application/json")
public String test() {
System.err.println(first);
System.err.println(list);
System.err.println(array);
return first;
}
}
application.yml file
this:
is:
array:
- "casa"
- "pesenna"
Results
The WithEnvCtrl.test method prints:
casa
null
null
null
The WithValueAnnotation.test method correctly sets the variable first with the first element of the array (casa). However, the annotations #Value on the attributes list and array cause the exception:
java.lang.IllegalArgumentException: Could not resolve placeholder 'this.is.array' in string value "${this.is.array}"
Here is an example project: property-array.
Many thanks in advance!

Solved by:
using the annotation #ConfigurationProperties;
declaring an attribute with the same name as the yml property;
defining the get method for the attribute;
initializing the attribute or defining the set method.
Here the code:
import java.util.List;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
#RestController
#ConfigurationProperties(prefix="this.is")
class WithValueAnnotation {
private List<String> array;
public List<String> getArray(){
return this.array;
}
public void setArray(List<String> array){
this.array = array;
}
#RequestMapping(value = "/test_cfg", method = { RequestMethod.GET,
RequestMethod.POST }, produces = "application/json")
public String test() {
System.err.println(array);
return array.toString();
}
}
Thanks #Quagaar.

Related

Getting Whitepage error, cant find my issue in REST API?

I am building a REST API to access a database and having trouble / consistently getting a whitepage error. Running in circles trying to find my error and/or my error in the flow or logic of the program.
Here is my application:
package com.skilldistillery.myRest;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
#SpringBootApplication
#ComponentScan(basePackages= {"com.skilldistillery.edgemarketing"})
#EntityScan("com.skilldistillery.edgemarketing")
#EnableJpaRepositories("com.skilldistillery.myRest.repositories")
public class MyRestApplication extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(MyRestApplication.class);
}
public static void main(String[] args) {
SpringApplication.run(MyRestApplication.class, args);
}
}
My controller:
package com.skilldistillery.myRest.controllers;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.skilldistillery.edgemarketing.entities.House;
import com.skilldistillery.myRest.services.HouseService;
#RestController
#RequestMapping("api")
#CrossOrigin({ "*", "http://localhost:4200" })
public class HouseController {
#Autowired
HouseService houseServ;
#GetMapping("index/{id}")
public House show(#PathVariable("id") Integer id) {
return houseServ.show(id);
}
}
My repo:
package com.skilldistillery.myRest.repositories;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import com.skilldistillery.edgemarketing.entities.House;
#Repository
public interface HouseRepo extends JpaRepository<House, Integer> {
}
My service:
package com.skilldistillery.myRest.services;
import java.util.List;
import org.springframework.stereotype.Service;
import com.skilldistillery.edgemarketing.entities.House;
#Service
public interface HouseService {
List<House> index();
House show(Integer id);
}
And my ServiceImpl:
package com.skilldistillery.myRest.services;
import java.util.Optional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.skilldistillery.edgemarketing.entities.House;
import com.skilldistillery.myRest.repositories.HouseRepo;
#Service
public class HouseServiceImpl {
#Autowired
HouseRepo hRepo;
public House show(Integer id) {
Optional<House> opt = hRepo.findById(id);
House house = null;
if (opt.isPresent()) {
house = opt.get();
}
return house;
}
}
It compiles and launches but via postman and browser, I am getting whitepage errors. I've scoured the internets trying to understand where I'm going wrong but not finding it. Please advise.
You can use the following solution.
Change your main class to the following code
#SpringBootApplication
public class MyrestapplicationApplication {
public static void main(String[] args) {
SpringApplication.run(MyrestapplicationApplication.class, args);
}
}
Then create a separate class for your configurations.As well as running away from tight coupled architecture.
#Configuration
#EntityScan("com.skilldistillery.edgemarketing.entities")
#EnableJpaRepositories("com.skilldistillery.myRest.repositories")
public class BusinessConfig {
#Bean
public HouseService houseService(final HouseRepo houseRepo){
return new HouseServiceImpl(houseRepo);
}
}
Your controller will then change to the following.Utilising Dependency Injection
#RestController
#RequestMapping("api")
#CrossOrigin({ "*", "http://localhost:4200" })
public class HouseController {
private HouseService houseServ;
public HouseController(HouseService houseServ) {
this.houseServ = houseServ;
}
#GetMapping(value = "index/{id}",produces = MediaType.APPLICATION_JSON_VALUE,consumes = MediaType.APPLICATION_JSON_VALUE)
public House show(#PathVariable("id") Integer id) {
return houseServ.show(id);
}
}
HouseServiceImpl should also implement HouseService
public class HouseServiceImpl implements HouseService{
private HouseRepo hRepo;
public HouseServiceImpl(HouseRepo hRepo) {
this.hRepo = hRepo;
}
#Override
public List<House> index() {
return null;
}
public House show(Integer id) {
Optional<House> opt = hRepo.findById(id);
House house = new House();
if (opt.isPresent()) {
house = opt.get();
}
return house;
}
}
*NB - don't forget to remove the following configs #Autowired,#Repository as they are now handled within the BusinessConfig class.More Beans can be defined in the BusinessConfig Class

pointcut for method in parent abstract class

I have a scenario where my method to be intercepted is in the parent class and is not overridden in the pointcut class.
Here is the sample classes:
public abstract class A{
#RequestMapping(value = "/data", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
public String getData(#RequestBody String request) throws Exception {
return "dummy";
}
}
#RestController
public class B extends A {
}
My Aspect is defined as:
#Aspect
#Component
public class RestCallLogger {
#Pointcut("within(com.test..*) && within(#org.springframework.web.bind.annotation.RestController *)")
public void restControllers() {
}
#Pointcut("#annotation(org.springframework.web.bind.annotation.RequestMapping)")
public void requestMappingAnnotations() {
}
#Around("restControllers() && requestMappingAnnotations()")
public Object onExecute(ProceedingJoinPoint jp) throws Throwable {
Object result = null;
try {
result = jp.proceed();
} catch (Exception ex) {
throw ex;
}
return result;
}
}
But its not working. If I mark class A with Annotation #RestController and make it concrete, then it works.
The question is how can I create a "pointcut for method in parent abstract class"?
PS: I can not change the hierarchy of the code as its the existing code.
For me this works. Here is an MCVE:
package de.scrum_master.app;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
public abstract class A {
#RequestMapping(value = "/data", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
public String getData(#RequestBody String request) throws Exception {
return request;
}
}
package de.scrum_master.app;
import org.springframework.web.bind.annotation.RestController;
#RestController
public class B extends A {}
package de.scrum_master.app;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
#Configuration
#EnableAspectJAutoProxy(proxyTargetClass = true)
#ComponentScan(basePackages = { "de.scrum_master" })
public class Application2 {
public static void main(String[] args) throws Exception {
ApplicationContext appContext = new AnnotationConfigApplicationContext(Application2.class);
B b = (B) appContext.getBean("b");
System.out.println(b.getData("bbb"));
A a = (A) appContext.getBean("b");
System.out.println(a.getData("aaa"));
}
}
package de.scrum_master.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
#Aspect
#Component
public class RestCallLogger {
#Pointcut("within(de.scrum_master..*) && #target(org.springframework.web.bind.annotation.RestController)")
public void restControllers() {}
#Pointcut("#annotation(org.springframework.web.bind.annotation.RequestMapping)")
public void requestMappingAnnotations() {
}
#Around("restControllers() && requestMappingAnnotations()")
public Object onExecute(ProceedingJoinPoint jp) throws Throwable {
System.out.println(jp);
Object result = null;
try {
result = jp.proceed();
} catch (Exception ex) {
throw ex;
}
return result;
}
}
The console log says:
execution(String de.scrum_master.app.A.getData(String))
bbb
execution(String de.scrum_master.app.A.getData(String))
aaa
What is different in your case?

Custom deserializer for requests with content-type application/x-www-form-urlencoded in Spring Boot

I want to write a custom deserializer for some parameters in the requests of type application/x-www-form-urlencoded like used in case of requests of type application/json, with #JsonDeserialize(using = AbcDeserializer.class) annotation. I am using spring boot and Jackson, although I figured out that Jackson is not used here.
I tried figuring out how spring deserializes object by default. But couldn't find a way.
How does spring deserialize a request of type application/x-www-form-urlencoded by default?
Can I override this deserialization, preferrably by using some annotation on parameters that need special handling?
My solution is based on custom ConditionalGenericConverter. It works with #ModelAttribute. Let's see whole implementation.
Application bootstrap example.
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Configuration;
import org.springframework.format.FormatterRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
#SpringBootApplication
public class DemoApplication {
#Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
#Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(new Base64JsonToObjectConverter());
}
}
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
Here is custom annotation.
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
#Retention(RetentionPolicy.RUNTIME)
public #interface Base64Encoded {
}
Next we need implementation of the converter. As you can see, converter converts only String -> Object, where Object field must be annotated with Base64Encoded annotation.
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.core.convert.ConversionFailedException;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.ConditionalGenericConverter;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.util.Base64;
import java.util.Collections;
import java.util.Set;
#Component
public class Base64JsonToObjectConverter implements ConditionalGenericConverter {
private final ObjectMapper objectMapper;
private final Base64.Decoder decoder;
public Base64JsonToObjectConverter() {
this.objectMapper = new ObjectMapper();
this.decoder = Base64.getDecoder();
}
#Override
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
return targetType.hasAnnotation(Base64Encoded.class);
}
#Override
public Set<ConvertiblePair> getConvertibleTypes() {
return Collections.singleton(new ConvertiblePair(String.class, Object.class));
}
#Override
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (source == null) {
return null;
}
String string = (String) source;
try {
byte[] decodedValue = this.decoder.decode(string);
return this.objectMapper.readValue(decodedValue, targetType.getType());
} catch (IllegalArgumentException | IOException e) {
throw new ConversionFailedException(sourceType, targetType, source, e);
}
}
}
Here is an example of POJO (see the annotated field) and REST controller.
import com.example.demo.Base64Encoded;
public class MyRequest {
private String varA;
#Base64Encoded
private B varB;
public String getVarA() {
return varA;
}
public void setVarA(String varA) {
this.varA = varA;
}
public B getVarB() {
return varB;
}
public void setVarB(B varB) {
this.varB = varB;
}
}
import com.example.demo.domain.MyRequest;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
#RestController
public class DemoController {
#RequestMapping(path = "/test", method = RequestMethod.POST)
public MyRequest test(#ModelAttribute MyRequest myRequest) {
return myRequest;
}
}

I can't get value of a property in spring-boot application

I am coding in spring-boot. I tried to get the value of properties.properties in other packages without success. For example in the classe ClassUtils.java, the value of testValue is always null
This is my project
This is my code:
package com.plugins.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import com.plugins.domain.ClassUtils;
#RestController
public class SearchContactController {
#Value("${environnement.test}")
String testValue;
#Value("${environnement.url}")
String urlValue;
#RequestMapping(value = "/test")
public String pingRequest() {
System.out.println("value ===> " + testValue + " /// " + urlValue);
return "test !" + ClassUtils.getTestValue();
}
}
This is my second class, where I can't get the value of testValue variable:
package com.plugins.domain;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
#Component
public class ClassUtils {
#Value("${environnement.test}")
static String testValue;
public static String getTestValue(){
return "The return "+testValue;
}
}
This is my springApp.java
package com.plugins;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class SpringBootVideApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootVideApplication.class, args);
}
}
Enable #ComponentScan({"com.plugins"}) , in Application
To access the properties defined in application.properties
myapp.url="xxxxxx"
in your class
#Value("${myapp.url}")
private String testValue;
but this cannot be a static variable, if it is a static variable you do some hack like this, by defining setter method
private static String testValue;
#Value("${myapp.url}")
public void testValue(String value) {
testValue = value;
}
I resolve this issue by addin #Autowired in the class which use the method of the other class this is a snippet
// Class: SearchContactController.java
#Autowired
ClassUtils cd;
#RequestMapping(value = "/ping")
public String pingRequest() {
return "Ping OK !" + cd.getTestValue();
}

Another class not mapped In Spring Boot application

Im studying the Spring doc in https://docs.spring.io/spring-boot/docs/current/reference/html/getting-started-first-application.html. When I create another class, the methods are not mapped.
import org.springframework.boot.*;
import org.springframework.boot.autoconfigure.*;
import org.springframework.stereotype.*;
import org.springframework.web.bind.annotation.*;
#RestController
#EnableAutoConfiguration
public class ExampleError {
#RequestMapping("/testmap")
public String test() {
return "TEST 2";
}
}
My main class works fine:
import org.springframework.boot.*;
import org.springframework.boot.autoconfigure.*;
import org.springframework.stereotype.*;
import org.springframework.web.bind.annotation.*;
#RestController
#EnableAutoConfiguration
public class Example {
#RequestMapping("/")
String home() {
return "Hello World!";
}
#RequestMapping("/test")
String teste() {
return "TEST 1";
}
public static void main(String[] args) throws Exception {
SpringApplication.run(Example.class, args);
}
}
Can someone clarify me?
Regards

Categories