Sending Map with Feign Client - spring-boot

I am trying to send request with Feign client, my request includes Map<String, String>. While sending request, Feign client throws an exception. An exception is:
class java.util.LinkedHashMap is not a type supported by this encoder.
Detail exception message is;
There is my request class:
#Data
#SuperBuilder
#EqualsAndHashCode(callSuper = true)
#NoArgsConstructor
#AllArgsConstructor
#ToString(callSuper = true)
public class DocumentRequest extends BaseExternalRequest {
private Map<String, String> metaData;
private String documentTypeId;
}
And this is consumer request model;
#Data
#SuperBuilder
#EqualsAndHashCode(callSuper = false)
#NoArgsConstructor
#AllArgsConstructor
#ToString(callSuper = true)
public class DocumentRequest extends BaseExternalRequest {
private Map<String, String> metaData;
private String documentTypeId;
}
Cleint Controller endpoint:
#PostMapping(value = "/upload")
public ResponseEntity<BaseResponse<String>> uploadDocument(#RequestPart("request") DocumentRequest request, #RequestPart("file") MultipartFile file) {
request = dysBaseRequestUtil.fillDysBaseRequest(request);
return dysDocumentClient.uploadDocument(request);
}
Feign Client interface;
#PostMapping(value = "/upload")
ResponseEntity<BaseResponse<String>> uploadDocument(#RequestPart DocumentRequest request);
Consumer controller;
#PostMapping(value = "/upload")
#RestDMSLogger(code = EndpointCodeConstants.UPLOAD_DOCUMENT)
public ResponseEntity<BaseResponse<String>> uploadDocument(#RequestPart DocumentRequest request) {
String insertedEntityId = documentService.uploadDocument(documentMapper.documentRequestToDocumentDto(request));
return ResponseEntity.ok(new BaseResponse<>(insertedEntityId));
}
How can I get rid of this exception.

Related

Mongo Template querying the wrong collection

I have a mongodb springboot application that is connected to 2 different databases, that have the same collection names and database names but different uris.
Here is my application.properties
spring.data.mongodb.uri = uri
spring.data.mongodb.secondDB.uri = uri
spring.data.mongodb.database = database_name
spring.data.mongodb.secondDB.database = database_name
My AppConfiguration file
#Configuration
public class MultipleMongoConfig {
#Primary
#Bean(name = "newdb1Properties")
#ConfigurationProperties(prefix = "spring.data.mongodb")
public MongoProperties getNewDb1Props() throws Exception {
return new MongoProperties();
}
#Bean(name = "newdb2Properties")
#ConfigurationProperties(prefix = "spring.data.mongodb.secondDB")
public MongoProperties getNewDb2Props() throws Exception {
return new MongoProperties();
}
#Primary
#Bean(name = "newdb1MongoTemplate")
public MongoTemplate newdb1MongoTemplate() throws Exception {
return new MongoTemplate(newdb1MongoDatabaseFactory(getNewDb1Props()));
}
#Bean(name ="newdb2MongoTemplate")
public MongoTemplate newdb2MongoTemplate() throws Exception {
return new MongoTemplate(newdb2MongoDatabaseFactory(getNewDb2Props()));
}
#Primary
#Bean
public MongoDatabaseFactory newdb1MongoDatabaseFactory(MongoProperties mongo) throws Exception {
return new SimpleMongoClientDatabaseFactory(
mongo.getUri()
);
}
#Bean
public MongoDatabaseFactory newdb2MongoDatabaseFactory(MongoProperties mongo) throws Exception {
return new SimpleMongoClientDatabaseFactory(
mongo.getUri()
);
}
Then I set up config files for each data source
#Configuration
#EnableMongoRepositories(basePackages = {"com.example.app.firstDatabse.Repository"},
mongoTemplateRef = "newdb1MongoTemplate"
)
public class NewDb1Config {
}
and
#Configuration
#EnableMongoRepositories(basePackages = {"com.example.app.secondDatabse.Repository"},
mongoTemplateRef = "newdb2MongoTemplate"
)
public class NewDb1Config {
}
For Model I have the following
#AllArgsConstructor
#NoArgsConstructor
#ToSting
#Document(collection = "coll")
public class FirstModel{
#Id
public String id;
#Field("f_name")
public String firstName;
#Field("l_name")
public String lastName;
#Field("age")
public int age;
#Field("gender")
public String gender;
}
and my second Model is the same
#AllArgsConstructor
#NoArgsConstructor
#ToSting
#Document(collection = "coll")
public class SecondModel{
#Id
public String id;
#Field("f_name")
public String firstName;
#Field("l_name")
public String lastName;
#Field("age")
public int age;
#Field("gender")
public String gender;
}
My controller
#ResController
#RequestMapping("/controller")
public class Controller{
#Autowired
private FirstDataabseRepository repo;
#Autowired
private SecondDataabseRepository repo;
#Resource
private MongoTemplate mongoTemplate;
#RequestMapping("/findByName")
public List<SecondModel> findByName(){
Criteria criteria = new Criteria();
criteria = Criteria.where("f_name").is("John");
Query q = new Query(criteria);
List<SecondModel> results = mongoTemplate.find(q,SecondModel.class);
return results;
}
}
So the results show the results of the first collection not the second one. What do I need to do for mongoTemplate to query the second collection not the first one.
Solution
I have to add a qualifier and have a mongoTemplate for each collection
#ResController
#RequestMapping("/controller")
public class Controller{
#Autowired
private FirstDataabseRepository repo;
#Autowired
private SecondDataabseRepository repo;
#Resource
#Qualifier(value="newdb1MongoTemplate")
private MongoTemplate mongoTemplate;
#Resource
#Qualifier(value="newdb2MongoTemplate")
private MongoTemplate mTemplate;
#RequestMapping("/findByName")
public List<SecondModel> findByName(){
Criteria criteria = new Criteria();
criteria = Criteria.where("f_name").is("John");
Query q = new Query(criteria);
List<SecondModel> results = mTemplate.find(q,SecondModel.class);
return results;
}
}

How to have access to the POJO returned by a RestController method (endpoint) in Spring Boot?

I want to be able to modify the POJO returned by a RestController method (or endpoint) before it gets serialized into the HttpServletResponse as a stream of data. But I want to be able to do it outside the controller method code (as a middleware).
I have tried to do it using a HandlerInterceptor but I do not have access there to the POJO. I have also tried using AOP but the Pointcut was never called.
#RestController
public class TestController {
#GetMapping("/test")
public Resource<User> getTest() {
Resource<User> resource = new Resource<>();
resource.setData(new User("test user"));
return resource;
}
#Builder
#Getter
#Setter
#AllArgsConstructor
static class User {
private String username;
}
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor
static class Resource<T> {
private T data;
private Set<String> errors;
}
}
I want to be able to add a list of errors (if needed) to the Resource returned by the Controller after the controller performs its own logic and returns.
To change the object after returned from #RestController method but before it is written to the HTTP response, you can implement ResponseBodyAdvice and declared it as #ControllerAdvice bean:
#ControllerAdvice
public static class Foo implements ResponseBodyAdvice {
#Override
public boolean supports(MethodParameter returnType, Class converterType) {
return true;
}
#Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
//body is the object return from the #RestController method
//Cast it and modify it accordingly.
if(body instanceof Resource) {
Resource res = (Resource)body;
//Modify it .... blablablba
}
return body;
}
}

Post json data and file using ResponseEntity<>

I am trying to upload json data and image to a database of a form using spring rest and hibernate. I tried to test it using POSTMAN by setting form-data in body and content-type as application/json in header, but i am getting http error 400. I also tried using #RequestPart but didnt not work. I searched but could not find an example using ResponseEnity<>. I think i am doing something wrong in controller class. Please someone help me.
Without the file part i am able to add json data to db using this.
#RequestMapping(value = "/users", method = RequestMethod.POST, produces ="application/json")
public ResponseEntity<User> createAparts( #RequestBody User user) {
if (user == null) {
return new ResponseEntity<User>(HttpStatus.BAD_REQUEST);
}
userService.addAparts(user);
return new ResponseEntity<User>(user, HttpStatus.CREATED);
}
Below are the related code to issue.
model
#Entity
#Table(name = "User")
#JsonIgnoreProperties({"hibernateLazyInitializer", "handler", "ignoreUnknown = true"})
public class User{
#Id
#Column(name = "id")
#GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;
#Column(name = "Name")
private String Name;
#Column(name = "file_data")
private byte[] file_data;
#Column(name = "filename")
private String filename;
#JsonCreator
public ApartsData(#JsonProperty("id") int id,
#JsonProperty("Name") String Name,
#JsonProperty("filename") String filename,
#JsonProperty("file_data") byte[] file_data){
this.ad_id = ad_id;
this.Name = Name;
this.filename= filename;
this.file_data = file_data;
}
public User(){
}
DAO
#Repository
public class UserDaoImpl implements UserDao{
private SessionFactory sessionFactory;
public void setSessionFactory(SessionFactory sessionFactory){
this.sessionFactory = sessionFactory;
}
#Override
public void addUser(User user) {
Session session = this.sessionFactory.getCurrentSession();
session.persist(user);
}
}
Service
#Service
public class UserServiceImpl implements UserService {
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
#Override
#Transactional
public void addUser(User user) {
this.userDao.addUser(user);
}
}
controller
#RestController
public class UserController {
private UserService userService;
#Autowired(required=true)
#Qualifier(value="userService")
public void setUserService(UserService userService){
this.userService = userService;
}
#RequestMapping(value = "/users", method = RequestMethod.POST,
produces ="application/json")
public ResponseEntity<User> createApartsData(#RequestBody User user,
#RequestParam("file") MultipartFile file) {
HttpHeaders headers = new HttpHeaders();
if (user == null) {
return new ResponseEntity<User>(HttpStatus.BAD_REQUEST);
}
if (!file.isEmpty()) {
try {
user.setFilename(file.getOriginalFilename());
user.setFile_data(file.getBytes());
} catch (Exception e){
e.printStackTrace();
}
}
userService.addUser(user);
headers.add("User Created - ", String.valueOf(user.getid()));
return new ResponseEntity<User>(user, headers, HttpStatus.CREATED);
}
}
UPDATE:
I am able to make it work with #RequestParam. Please some help me to make it work with #RequestBody

No Converter Found Error when using Custom JsonSerializer

In a Spring application I want to use my own JsonSerializer with a RestController. My JsonSerializer is registered in a Jackson2ObjectMapperBuilder. So far so good, but when I annotate the respective fields with #JsonSerialize Spring MVC complains that there is no converter found for my class. Here is the code:
public class DurationAsHourSerializer extends JsonSerializer<Duration>{
#Override
public void serialize(Duration value, JsonGenerator gen, SerializerProvider serializers)
throws IOException, JsonProcessingException {
gen.writeNumber(value.toHours());
}
}
#Configuration
#EnableWebMvc
public class AppConfig {
#Bean
public Jackson2ObjectMapperBuilder jacksonBuilder() {
Jackson2ObjectMapperBuilder b = new Jackson2ObjectMapperBuilder();
SimpleModule module = new SimpleModule();
module.addSerializer(Duration.class, new DurationAsHourSerializer());
b.modulesToInstall(module);
return b;
}
}
#EqualsAndHashCode
#ToString
public class Order {
#Getter
#Setter
private String id;
#Getter
#Setter
#NotNull
private String customerId;
#Getter
#Setter
private ZonedDateTime validFrom;
#Getter
#Setter
private ZonedDateTime validTo;
#Getter
#Setter
#JsonSerialize(as=DurationAsHourSerializer.class)
private Duration maxDuration;
#Getter
#Setter
#JsonSerialize(as=DurationAsHourSerializer.class)
private Duration actDuration;
}
#RestController
#RequestMapping("order")
public class OrderController {
#RequestMapping(method = RequestMethod.POST)
public ResponseEntity<?> createOrder(#RequestBody #Valid Order order) {
HttpHeaders headers = new HttpHeaders();
headers.setLocation(ServletUriComponentsBuilder.fromCurrentRequest().path("/" + order.getId()).build().toUri());
return new ResponseEntity<>(null, headers, HttpStatus.CREATED);
}
#RequestMapping(method = RequestMethod.GET)
public Order getExample() {
Order order = new Order();
order.setMaxDuration(Duration.ofHours(10));
return order;
}
}
When I send a request to getExample(), I get the following error:
java.lang.IllegalArgumentException: No converter found for return
value of type: class com.sap.sptutorial.rest.Order at
org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.writeWithMessageConverters(AbstractMessageConverterMethodProcessor.java:178)
~[spring-webmvc-4.2.4.RELEASE.jar:4.2.4.RELEASE]
Why can't the Converter use my serializer, or why does my serializer make the otherwise existing converter unavailable?
PS: This is a follow up to How do I use a custom Serializer with Jackson? where I tried to use a Field Formatter for the same job, and learned that Formatters are not used for the Jackson Serialization.
It reason behind this could be, because there is already another object of Jackson2ObjectMapperBuilder constructed by Spring and it is still using it.
You need to make sure you make this #Bean as the default to be used by spring or use #PostConstruct rather that #Bean to modify the same instance of Jackson2ObjectMapperBuilder initialized by Spring
#Autowired
private Jackson2ObjectMapperBuilder builder;
............
#PostConstruct
public Jackson2ObjectMapperBuilder jacksonBuilder() {
Jackson2ObjectMapperBuilder b = new Jackson2ObjectMapperBuilder();
SimpleModule module = new SimpleModule();
module.addSerializer(Duration.class, new DurationAsHourSerializer());
b.modulesToInstall(module);
return b;
}
The error is that I used the wrong attribute with the #JsonSerialize annotation. Instead of #JsonSerialize(as=DurationAsHourSerializer.class) you have to declare the serializer to use with #JsonSerialize(using=DurationAsHourSerializer.class)

Spring MongoRepository is Null

I have the following code which attempts to save a POJO object (Actor) into MongoDB using Spring Mongo Repository, but the repository object is always Null. I have followed multiple examples but mainly this one
The POJO class:
#Document(collection = "actors")
public class Actor
{
#Id
private String id;
...
//constructor
//setters & getters
}
The repository:
public interface ActorRepository extends MongoRepository<Actor, String>
{
public Actor findByFNameAndLName(String fName, String lName);
public Actor findByFName (String fName);
public Actor findByLName(String lName);
}
The service that uses the repository:
#Service
public class ActorService
{
#Autowired
private ActorRepository actorRepository;
public Actor insert(Actor a)
{
a.setId(null);
return actorRepository.save(a);
}
}
And I access the service from a REST controller class:
#RestController
public class Controllers
{
private static final Logger logger = Logger.getLogger(Controllers.class);
private static final ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringMongoConfig.class);
private ActorService actorService = new ActorService();
#RequestMapping(value="/createActor", method=RequestMethod.POST)
public #ResponseBody String createActor(#RequestParam(value = "fName") String fName,
#RequestParam(value = "lName") String lName,
#RequestParam(value = "role") String role)
{
return actorService.insert(new Actor(null,fName,lName,role)).toString();
}
...
}
The error that I get is NullPointerException from this line: return actorRepository.save(a); in the ActorService.insert() method.
Any Idea why is this happening?
EDIT: Here is the Spring Configurations
#Configuration
public class SpringMongoConfig extends AbstractMongoConfiguration
{
#Bean
public GridFsTemplate gridFsTemplate() throws Exception
{
return new GridFsTemplate(mongoDbFactory(), mappingMongoConverter());
}
#Override
protected String getDatabaseName()
{
return "SEaaS";
}
#Override
#Bean
public Mongo mongo() throws Exception
{
return new MongoClient("localhost" , 27017 );
}
public #Bean MongoTemplate mongoTemplate() throws Exception
{
return new MongoTemplate(mongo(), getDatabaseName());
}
}
The problem is that you are not using Spring to get the ActorService dependency -instead you have manually instantiated the dependency using
private ActorService actorService = new ActorService();.
The following code is the easiest fix in order to inject the ActorService dependency into the controller.
#RestController
public class Controllers
{
private static final Logger logger = Logger.getLogger(Controllers.class);
private static final ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringMongoConfig.class);
#Autowired
private ActorService actorService;
#RequestMapping(value="/createActor", method=RequestMethod.POST)
public #ResponseBody String createActor(#RequestParam(value = "fName") String fName,
#RequestParam(value = "lName") String lName,
#RequestParam(value = "role") String role)
{
return actorService.insert(new Actor(null,fName,lName,role)).toString();
}
...
}

Resources