Spring: How to publish a multipart/form-data request to a Rabbit Queue - spring-boot

The application has an endpoint that consumes multipart/form-data and maps it to a MultipartFile obj. From there, we attempt to upload the file to the vendor. If its successful, nothing else occurs other than a HTTP.200 series
If there is a failure, we want to publish a message to a Rabbit Queue where fields in the message include things like "endpoint", "data", "headers" so that we can consume the messages and hit the endpoint at a later date.
The problem is that I can't deserialize the MultipartFile. Ive tried using ByteArrayResource as well but get the error No serializer found for class java.io.ByteArrayInputStream
Is there a better way to do this?
Also doing this approach because there are multiple endpoints that take in a file in different conventions, so being able to replicate the request and not have logic and just recall the endpoint is ideal
Ultimate question: How can I write value as string on a ByteArrayResource or MultipartFile obj with Jackson

Wouldn't it be easier to save the file to disk and send the path to the queue?
In any cases, ByteArrayResource should work if you add the converter:
#Bean
public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
ObjectMapper mapper = new ObjectMapper();
mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
MappingJackson2HttpMessageConverter converter = new
MappingJackson2HttpMessageConverter(mapper);
return converter;
}

Related

Spring: Which internal class inserts the #RequestBody parameter in controllers?

#RequestMapping(value = "/something", method = RequestMethod.POST, produces = "application/json")
public void triggerInvitations(#RequestBody String postBody)
Which Spring class does the actual task of reading the input stream and setting the request body in the parameter "postBody"?
Actually, I'm just getting the response status as 400 Bad Request and unable to see the actual cause. As such, I wanted to debug the Spring code and know exactly the reason why its a bad request. (If I don't have a #RequestBody parameter in the controller signature, control is able to reach the method)
I'm using Spring Boot 1.5.8
There are different HTTP Message converters used by Spring. This article will give you an overview of this and also list different message converters.
Turn on your springframework debugging in your application.properties:
logging.level.org.springframework = DEBUG
It should start to show you why it's not matching the request to your RequestMapping.

Spring Boot / Kafka Json Deserialization - Trusted Packages

I am just starting to use Kafka with Spring Boot & want to send & consume JSON objects.
I am getting the following error when I attempt to consume an message from the Kafka topic:
org.apache.kafka.common.errors.SerializationException: Error deserializing key/value for partition dev.orders-0 at offset 9903. If needed, please seek past the record to continue consumption.
Caused by: java.lang.IllegalArgumentException: The class 'co.orders.feedme.feed.domain.OrderItem' is not in the trusted packages: [java.util, java.lang]. If you believe this class is safe to deserialize, please provide its name. If the serialization is only done by a trusted source, you can also enable trust all (*).
at org.springframework.kafka.support.converter.DefaultJackson2JavaTypeMapper.getClassIdType(DefaultJackson2JavaTypeMapper.java:139) ~[spring-kafka-2.1.5.RELEASE.jar:2.1.5.RELEASE]
at org.springframework.kafka.support.converter.DefaultJackson2JavaTypeMapper.toJavaType(DefaultJackson2JavaTypeMapper.java:113) ~[spring-kafka-2.1.5.RELEASE.jar:2.1.5.RELEASE]
at org.springframework.kafka.support.serializer.JsonDeserializer.deserialize(JsonDeserializer.java:218) ~[spring-kafka-2.1.5.RELEASE.jar:2.1.5.RELEASE]
at org.apache.kafka.clients.consumer.internals.Fetcher.parseRecord(Fetcher.java:923) ~[kafka-clients-1.0.1.jar:na]
at org.apache.kafka.clients.consumer.internals.Fetcher.access$2600(Fetcher.java:93) ~[kafka-clients-1.0.1.jar:na]
I have attempted to add my package to the list of trusted packages by defining the following property in application.properties:
spring.kafka.consumer.properties.spring.json.trusted.packages = co.orders.feedme.feed.domain
This doesn't appear to make any differences. What is the correct way to add my package to the list of trusted packages for Spring's Kafka JsonDeserializer?
Since you have the trusted package issue solved, for your next problem you could take advantage of the overloaded
DefaultKafkaConsumerFactory(Map<String, Object> configs,
Deserializer<K> keyDeserializer,
Deserializer<V> valueDeserializer)
 and the JsonDeserializer "wrapper" of spring kafka
JsonDeserializer(Class<T> targetType, ObjectMapper objectMapper)
Combining the above, for Java I have:
new DefaultKafkaConsumerFactory<>(properties,
new IntegerDeserializer(),
new JsonDeserializer<>(Foo.class,
new ObjectMapper()
.registerModules(new KotlinModule(), new JavaTimeModule()).setSerializationInclusion(JsonInclude.Include.NON_NULL)
.setDateFormat(new ISO8601DateFormat()).configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false))));
Essentially, you can tell the factory to use your own Deserializers and for the Json one, provide your own ObjectMapper. There you can register the Kotlin Module as well as customize date formats and other stuff.
Ok, I have read the documentation in a bit more detail & have found an answer to my question. I am using Kotlin so the creation of my consumer looks like this with the
#Bean
fun consumerFactory(): ConsumerFactory<String, FeedItem> {
val configProps = HashMap<String, Any>()
configProps[ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG] = bootstrapServers
configProps[ConsumerConfig.GROUP_ID_CONFIG] = "feedme"
configProps[ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG] = StringDeserializer::class.java
configProps[ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG] = JsonDeserializer::class.java
configProps[JsonDeserializer.TRUSTED_PACKAGES] = "co.orders.feedme.feed.domain"
return DefaultKafkaConsumerFactory(configProps)
}
Now I just need a way to override the creation of the Jackson ObjectMapper in the JsonDeserializer so that it can work with my Kotlin data classes that don't have a zero-argument constructor :)

Spring data rest how to serve hal+json serialization for content-type json

We are using spring-data-rest to expose our repositories.
Choosing as default type application/hal+json produce the desired presentation of the resources and links, with content exposed as _embedded and links as _links.
Unfortunately, Firefox is not able to render application/hal+json, suggesting user to download the hal+json document if the user is navigating the api using the browser.
As a possible solution to this, we would like to support application/json as defaultMediaType and also hal+json if the user is explicitly requiring that.
Using application/json as defaultMediaType with spring-data-rest tough bypass the hal+json serialization provided by spring-hateoas, so the resources are displayed with "content" and "links".
I've tried to extends the RepositoryRestConfigurerAdapter, overriding the configureHttpMessageConverter this way
#Override
public void configureHttpMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new Jackson2HalModule());
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
MappingJackson2HttpMessageConverter halConverter = new TypeConstrainedMappingJackson2HttpMessageConverter(ResourceSupport.class);
halConverter.setObjectMapper(mapper);
halConverter.setSupportedMediaTypes(Arrays.asList(MediaType.APPLICATION_JSON, MediaTypes.HAL_JSON));
//make sure this is inserted first
messageConverters.add(0, halConverter);
super.configureHttpMessageConverters(messageConverters);
}
but the converter is not used to render the resources.
Any one has any suggestion on how to tackle this?

Possible bug in ResourceHttpMessageConverter

I've been experiencing a strange problem using the ResourceHttpMessageConverter in the latest Spring 3.2.4 version. I have an annotated controller that returns a Resource, in specific a UrlResource. This UrlResource is nothing more than a Request to another remote server that serves a pdf file. Usually the pdf is a small file (less than 1MB) but under some circumstances is larger. In case the file is large the client that contacts to my Controller can't download the file resulting in a connection closed error. The code I am using is the following
#Controller
#PreAuthorize(value = "isAuthenticated()")
public class TestController {
#ResponseBody
#RequestMapping(value="/report/", method = RequestMethod.GET,
produces = "application/pdf")
public Resource getReport() {
//Ignore the getResource method, it is not the problem
//this method returns an object of type UrlResource
return this.getResource();
}
}
Although there is a workaround using the StreamUtils class to copy the InputStream from the UrlResource to the OutputStream of the HttpServletResponse, I wanted to know for sure if there is anything else I could do to avoid that, and rely on the Spring MessageConverter infrastructure rather than reimplementing the same logic in my controller. Is there any spring developer around that can point me in the right direction if it is possible, or if this is a bug let me know so I can report it. Thanks!

RestTemplate "no suitable HttpMessageConverter"

i realize that something wired goes on with a project i try to create. I'm using the RestTemplate. And i'm trying to connect with a server and retrieve data. All went good until the time i decide to broke my program in more than one controller classes. Look what i did. In each one of these new classes i insert at each one of them:
RestTemplate restTemplate= new RestTemplate();
In order to retrieve data i'm using the JAXB annotations only(for data binding) and i can retrieve whatever i want. But when i'm trying to execute this line of code in the new class:
ResponseEntity<AClass> result = restTemplate.exchange("url",
HttpMethod.GET, entity, AClass.class);
I'm taking this exception: RestClientException "Could not extract response: no suitable HttpMessageConverter found for response type [AClass] and content type [application/xml]"
If i put it back in the previous class can be executed without problem. Really i don't understand why. Probably because i'm using in the new class a new RestTemplate.I try to declare different RestTemplates and also to declare this way:#Autowired but the problem remains. Should i declare something new in the dispatcher servlet?Moreover can i call in many classes the object restTemplate(RestTemplate restTemplate=new RestTemplate()). Should i declare it in a specific class and call it from there? What should i do?I expect your propositions.
How did you previously have your restTemplate instance set up? You have to register a MarshallingHttpMessageConverter with the RestTemplate instance, or it won't know how to unmarshal the XML. Something like this:
Jaxb2Marshaller jaxbMarshaller = new Jaxb2Marshaller();
// Make sure context paths includes AClass's package
jaxbMarshaller.setContextPaths("com.example.generated");
MarshallingHttpMessageConverter converter = new
MarshallingHttpMessageConverter(jaxbMarshaller, jaxbMarshaller);
restTemplate.setMessageConverters(Arrays.<HttpMessageConverter<?>> asList(converter));

Resources