Sending #Value annotated fields to a DTO layer returns null - spring-boot

I have a class which is composed of 2 different objects :
public class MyClass{
private OptionClass optionClass;
private ConstantClass constantClass;
public DocumentToSignRestRequest(OptionClass optionClass, ConstantClass constantClass) {
this.optionClass= optionClass;
this.constantClass= constantClass;
}
}
My first class is a classic POJO. My second class retrieve values from the application.properties file.
public class ConstantClass {
#Value("${api.url}")
private String hostName;
#Value("${sign.path}")
private String pathStart;
public ConstantClass () {
this.hostName= getHostName();
this.path = getPath();
}
I map MyClass with MyClassDto in order to call a service.
#PostMapping(
value="/sign",
consumes = { MediaType.APPLICATION_XML_VALUE, MediaType.APPLICATION_JSON_VALUE },
produces = { MediaType.APPLICATION_XML_VALUE, MediaType.APPLICATION_JSON_VALUE }
)
public MyClassRest prepareDocument(#RequestBody DocumentToPrepare documentToPrepare) throws Exception {
MyClassRest returnValue = new MyClassRest ();
ModelMapper modelMapper = new ModelMapper();
MyClassDto myClassDto = modelMapper.map(documentToPrepare, MyClassDto .class);
DocumentDto signedDocument = documentService.signDocument(documentDto);
returnValue = modelMapper.map(signedDocument, DocumentRest.class);
return returnValue;
}
My DTO class work fine and retrieve the OptionClass datas, but concerning the second Class, i obtain null as value, while i try to print it out in the service layer.

Your ConstantClass should be a Bean or a Component (as #cassiomolin says in comments)
#Component
public class ConstantClass {
private String hostName;
private String pathStart;
public ConstantClass (#Value("${api.url}") String url, #Value("${sign.path}") String path ) {
this.hostName = url;
this.pathStart = path;
}
// getters...
Then you can easily inject this component in your Controller and use it.
#Controller
public class YourController(){
private ConstantClass constantClass;
public YourController(ConstantClass constantClass){
this.constantClass = constantClass;
}
#PostMapping("...")
public MyClass post(.....){
.....
MyClass myclass = new MyClass(this.constantClass,...)
.....
}
}
note that Spring can autowire #Value and #Component, ... via the constructor; that can be very useful when you do unit-testing

Related

Is Spring #Component annotation used correctly?

The purpose of this question is to find out if the codes are written with the right approach. Let's do CRUD operations on categories and posts in the blog website project. To keep the question short, I shared just create and update side.
(Technologies used in the project: spring-boot, mongodb)
Let's start to model Category:
#Document("category")
public class Category{
#Id
private String id;
#Indexed(unique = true, background = true)
private String name;
#Indexed(unique = true, background = true)
private String slug;
// getter and setter
Abstract BaseController class and IController Interface is created for fundamental level save, delete and update operations. I shared below controller side:
public interface IController<T>{
#PostMapping("/save")
ResponseEntity<BlogResponse> save(T object);
#GetMapping(value = "/find-all")
ResponseEntity<BlogResponse> findAll();
#GetMapping(value = "/delete-all")
ResponseEntity<BlogResponse> deleteAll();
}
public abstract class BaseController<T extends MongoRepository<S,String>, S> implements IController<S> {
#Autowired
private T repository;
#Autowired
private BlogResponse blogResponse;
#PostMapping(value = "/save", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
public #ResponseBody ResponseEntity<BlogResponse> save(S object) {
try {
S model = (S) repository.save(object);
String modelName = object.getClass().getSimpleName().toLowerCase();
blogResponse.setMessage(modelName + " is saved successfully").putData(modelName, object);
} catch (DuplicateKeyException dke) {
return new ResponseEntity<BlogResponse>(blogResponse.setMessage("This data is already existing!!!"), HttpStatus.BAD_REQUEST);
} catch (Exception e) {
return new ResponseEntity<BlogResponse>(blogResponse.setMessage(e.getMessage()), HttpStatus.INTERNAL_SERVER_ERROR);
}
return new ResponseEntity<BlogResponse>(blogResponse, HttpStatus.OK);
}
// delete, findAll and other controllers
#RestController
#RequestMapping(value = "category")
#RequestScope
public class CategoryController extends BaseController<ICategoryRepository, Category>{
// More specific opretions like findSlug() can be write here.
}
And finally BlogResponce component is shared below;
#Component
#Scope("prototype")
public class BlogResponse{
private String message;
private Map<String, Object> data;
public String getMessage() {
return message;
}
public BlogResponse setMessage(String message) {
this.message = message;
return this;
}
public BlogResponse putData(String key, Object object){
if(data == null)
data = new HashMap<String,Object>();
data.put(key,object);
return this;
}
public Map<String,Object> getData(){
return data;
}
#Override
public String toString() {
return "BlogResponse{" +
"message='" + message + '\'' +
", data=" + data +
'}';
}
}
Question: I am new spring boot and I want to move forward by doing it right. BlogResponse is set bean by using #Component annotation. This doc said that other annotations like #Controller, #Service are specializations of #Component for more specific use cases. So I think, I cant use them. BlogResponse is set prototype scope for create new object at each injection. Also it's life end after response because of #RequestScope. Are this annotations using correcty? Maybe there is more effective way or approach. You can remark about other roughness if it existing.

Can I return DTO and domain entities from services?

I have a spring-boot application and I use DTO like that:
Service
#Service
public class UnitOfMeasureServiceImpl implements IUnitOfMeasureService {
private final IUnitsOfMeasureRepository unitOfMeasureRepository;
#Autowired
public UnitOfMeasureServiceImpl(IUnitsOfMeasureRepository unitOfMeasureRepository) {
this.unitOfMeasureRepository = unitOfMeasureRepository;
}
#Override
public UnitOfMeasureDTO getUnitOfMeasureById(UUID id) {
Optional<UnitOfMeasure> optionalUnitOfMeasure = unitOfMeasureRepository.findById(id);
if (!optionalUnitOfMeasure.isPresent()){
// throw new ComponentNotFoundException(id);
return null;
}
return UnitOfMeasureDTO.factory(optionalUnitOfMeasure.get());
}
dto:
#Data
#JsonInclude(JsonInclude.Include.NON_NULL)
public class UnitOfMeasureDTO {
private String id;
private String name;
private String description;
private String sourceInfoCompanyName;
private String originalId;
public static UnitOfMeasureDTO factory(UnitOfMeasure unitOfMeasure) {
UnitOfMeasureDTO dto = new UnitOfMeasureDTO();
dto.id = unitOfMeasure.getId().toString();
dto.name = unitOfMeasure.getName();
dto.description = unitOfMeasure.getDescription();
dto.sourceInfoCompanyName = unitOfMeasure.getSourceInfo().getSourceCompany().getName();
dto.originalId = unitOfMeasure.getOriginalId();
return dto;
}
}
controller:
#RestController
#RequestMapping(UnitOfMeasureController.BASE_URL)
public class UnitOfMeasureController {
public static final String BASE_URL = "/api/sust/v1/unitOfMeasures";
private final IUnitOfMeasureService unitOfMeasureService;
public UnitOfMeasureController(IUnitOfMeasureService unitOfMeasureService) {
this.unitOfMeasureService = unitOfMeasureService;
}
#GetMapping(path = "/{id}")
#ResponseStatus(HttpStatus.OK)
public UnitOfMeasureDTO getUnitOfMeasureDTO(#PathVariable("id") UUID id) {
UnitOfMeasureDTO unitOfMeasureDTO = unitOfMeasureService.getUnitOfMeasureById(id);
return unitOfMeasureDTO;
}
So in my service I have getUnitOfMeasureById(UUID id) that return a UnitOfMeasureDTO.
Now I need to call, from another service, getUnitOfMeasureById(UUID id) that return the domain entity UnitOfMeasure. I think it's correct to call a service method from another service (not a controller method!) and the separation between business logic is at the service layer. So is it correct to have 2 methods: getUnitOfMeasureDTOById and getUnitOfMeasureById in the service? (getUnitOfMeasureDTOById call getUnitOfMeasureById to avoid code duplication)

Marshal list of objects of different types with JAXB

I work with Spring and JAXB and want to marshal a list of different DTO Objects so
the required XML response should be like :
<root>
<dto_list>
<dto1>
<name>xxx</name>
</dto1>
<dto2>
<location>xxx</location>
</dto2>
</dto_list>
</root>
Assuming the Class Objects are:
#XmlRootElement(name = "Dto1")
#XmlAccessorType(XmlAccessType.NONE)
public class Dto1 {
#XmlElement
private String name;
// setter/getters
}
and
#XmlRootElement(name = "Dto2")
#XmlAccessorType(XmlAccessType.NONE)
public class Dto2 {
#XmlElement
private String location;
// setter/getters
}
and the wrapper class:
#XmlRootElement(name = "root")
#XmlAccessorType(XmlAccessType.NONE)
public class DTOsWrapper {
private List<Object> dto;
public void setDto(List<Object> dto) {
this.dto = dto;
}
#XmlElementWrapper(name = "dto_list")
#XmlElements({
#XmlElement(name = "dto1", type = Dto1.class),
#XmlElement(name = "dto2", type = Dto2.class)
})
public List<Object> getDto() {
return dto;
}
}
and the endpoint:
#RestController
public class DTOEndpoint {
#Autowired
private IDTOService service;
#RequestMapping(value = "/restxml", produces = "application/xml")
public Object retrieveAllDTOs() {
DTOsWrapper o = service.findDtos(); //returns a DTOsWrapper obj of a list containing Dto objs, i.e Dto1, Dto2 etc
return o;
}
I get {"error": "org.springframework.http.converter.HttpMessageConversionException: Could not instantiate JAXBContext for class [class <>to.DTOsWrapper]: 1 counts of IllegalAnnotationExceptions; anyone have a clue?

Spring boot - setters on session scoped component not working from singleton service - fields are null

I have a simple service behind a REST controller in Spring Boot. The service is a singleton (by default) and I am autowiring a session-scoped bean component used for storing session preferences information and attempting to populate its values from the service. I call setters on the autowired component, but the fields I am setting stay null and aren't changed.
Have tried with and without Lombok on the bean; also with and without implementing Serializable on FooPref; also copying properties from FooPrefs to another DTO and returning it; also injecting via #Autowired as well as constructor injection with #Inject. The fields stay null in all of those cases.
Running Spring Boot (spring-boot-starter-parent) 1.5.6.RELEASE, Java 8, with the spring-boot-starter-web.
Session-scoped component:
#Component
#SessionScope(proxyMode = ScopedProxyMode.TARGET_CLASS)
#Data
#NoArgsConstructor
public class FooPrefs implements Serializable {
private String errorMessage;
private String email;
private String firstName;
private String lastName;
}
REST Controller:
#RestController
#RequestMapping("/api/foo")
public class FooController {
#Autowired
private FooPrefs fooPrefs;
private final FooService fooService;
#Inject
public FooController(FooService fooService) {
this.fooService = fooService;
}
#PostMapping(value = "/prefs", consumes = "application/json", produces = "application/json")
public FooPrefs updatePrefs(#RequestBody Person person) {
fooService.updatePrefs(person);
// These checks are evaluating to true
if (fooPrefs.getEmail() == null) {
LOGGER.error("Email is null!!");
}
if (fooPrefs.getFirstName() == null) {
LOGGER.error("First Name is null!!");
}
if (fooPrefs.getFirstName() == null) {
LOGGER.error("First Name is null!!");
}
return fooPrefs;
}
}
Service:
#Service
#Scope(value = "singleton")
#Transactional(readOnly = true)
public class FooService {
#Autowired
private FooPrefs fooPrefs;
#Inject
public FooService(FooRepository fooRepository) {
this.fooRepository = fooRepository;
}
public void updatePrefs(Person person) {
fooRepository.updatePerson(person);
//the fields below appear to getting set correctly while debugging in the scope of this method call but after method return, all values on fooPrefs are null
fooPrefs.setEmail(person.getEmail());
fooPrefs.setFirstName(person.getFirstName());
fooPrefs.setLastName(person.getLastName());
}
}
I discovered my problem. Fields were being added to my FooPrefs session-managed object and were breaking my client. The setters were actually working and being nulled out by some error handling code.
Edits per below fixed the JSON serialization problems:
Session-scoped component (no change)
New Dto
#Data
#NoArgsConstructor
public class FooPrefsDto {
private String errorMessage;
private String email;
private String firstName;
private String lastName;
}
Controller (updated)
#RestController
#RequestMapping("/api/foo")
public class FooController {
private final FooService fooService;
#Inject
public FooController(FooService fooService) {
this.fooService = fooService;
}
#PostMapping(value = "/prefs", consumes = "application/json", produces = "application/json")
public FooPrefsDto updatePrefs(#RequestBody Person person) {
FooPrefsDto result = fooService.updatePrefs(person);
// results coming back correctly now
if (result.getEmail() == null) {
LOGGER.error("Email is null!!");
}
if (result.getFirstName() == null) {
LOGGER.error("First Name is null!!");
}
if (result.getFirstName() == null) {
LOGGER.error("First Name is null!!");
}
return result;
}
}
Service (updated)
#Service
#Scope(value = "singleton")
#Transactional(readOnly = true)
public class FooService {
#Autowired
private FooPrefs fooPrefs;
#Inject
public FooService(FooRepository fooRepository) {
this.fooRepository = fooRepository;
}
public FooPrefsDto updatePrefs(Person person) {
fooRepository.updatePerson(person);
//the fields below appear to getting set correctly while debugging in the scope of this method call but after method return, all values on fooPrefs are null
fooPrefs.setEmail(person.getEmail());
fooPrefs.setFirstName(person.getFirstName());
fooPrefs.setLastName(person.getLastName());
return getFooPrefsDto();
}
private FooPrefsDto getFooPrefsDto() {
FooPrefsDto retDto = new FooPrefsDto();
retDto.setEmail(fooPrefs.getEmail());
retDto.setLastName(fooPrefs.getLastName());
retDto.setFirstName(fooPrefs.getFirstName());
return retDto;
}
}

How can I load propeties in a Map with SpringBoot?

I try to initialize a Map in my SpringBoot application but I am doing something wrong.
My config.properties:
myFieldMap.10000.fieldName=MyFieldName
myFieldMap.10000.fieldName2=MyFieldName2
myFieldMap.10001.fieldName=MyFieldName
myFieldMap.10001.fieldName2=MyFieldName2
myFieldMap.10002.fieldName=MyFieldName
myFieldMap.10003.fieldName2=MyFieldName2
...
(Isn't it possible to use some kind of bracket notation like myFieldMap[10001].fieldName for maps (I saw it used for lists).
I tried with my MyConfig.class:
#PropertySource("classpath:config.properties")
#Component
public class MyConfig {
private java.util.Map<Integer, MyMapping> theMappingsMap = new HashMap<Integer, MyMapping>();
public Map<String, MyMapping> getTheMappingsMap() {
return theMappingsMap;
}
public void setTheMappingsMap(Map<String, MyMapping> theMappingsMap) {
this.theMappingsMap= theMappingsMap;
}
public class MyMapping {
private String fieldName;
private String fieldName2;
public String getFieldName() {
return fieldName;
}
public String getFieldName2() {
return fieldName2;
}
public void setFieldName(final String fieldName) {
this.fieldName = fieldName;
}
public void setFieldName2(final String fieldName) {
this.fieldName2 = fieldName;
}
}
}
How do I have to adapt my code to let SpringBoot initialize my configuration (Map) with the definitions in the config.properties file?
You are missing #ConfigurationProperties annotation. Try this
#PropertySource("classpath:config.properties")
#Configuration
#ConfigurationProperties
public class MyConfig {
private java.util.Map<String, MyMapping> myFieldMap = new HashMap<>();
....
}
Another issue with your code is, if you want to make MyMapping class as an inner class of MyConfig, then you need to declare it as static. Or else you can make it as a separate class.

Resources