In Apache Camel I expose a REST service, take its input to call a SOAP service and then I'd like to marshal the SOAP response to JSON. My RouteBuilder looks roughly like this:
rest("/api")
.get("/client/{id}")
.to("direct:getClient");
from("direct:getClient")
.log(LoggingLevel.INFO, "Getting client with id ${id}")
.process(new GetClientProcessor())
.marshal().jaxb()
.to("spring-ws:http://localhost:9000/searchClient?soapAction=search")
.process(new ClientProcessor())
.marshal().json(JsonLibrary.Jackson);
I get the following error while marshalling the result to JSON:
com.fasterxml.jackson.databind.JsonMappingException: No serializer found for class org.apache.camel.converter.stream.InputStreamCache and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS)
at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:275)
at com.fasterxml.jackson.databind.SerializerProvider.mappingException(SerializerProvider.java:1110)
at com.fasterxml.jackson.databind.SerializerProvider.reportMappingProblem(SerializerProvider.java:1135)
at com.fasterxml.jackson.databind.ser.impl.UnknownSerializer.failForEmpty(UnknownSerializer.java:69)
at com.fasterxml.jackson.databind.ser.impl.UnknownSerializer.serialize(UnknownSerializer.java:32)
at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:292)
...
I know why this is happening, as I have turned on stream caching by default. However, I don't know how to fix this without turning off stream caching.
I have searched through the Camel documentation, mailing lists and fora, but I haven't found useful information sofar.
I finally solved it. The problem had nothing to do with described routes, rather the global rest configuration:
RestConfiguration restConfiguration = new RestConfiguration();
restConfiguration.setComponent("servlet");
restConfiguration.setBindingMode(RestConfiguration.RestBindingMode.json);
restConfiguration.setHost("localhost");
restConfiguration.setPort(serverPort);
camelContext.setRestConfiguration(restConfiguration);
The third line, setting the binding mode, is unnecessary as I state explicitly when I'd like to map to JSON and also which framework I use. After I remove this line everything works like a charm.
At this moment I don't exactly know how or why this has solved my problem, but I'm happy it did ;)
Related
I am creating an controller where there is certain attributes in json which is an doing in postman a POST request like this if all attributes are posted then its fine
if one then is missing then it would look like this
i want this response when some attribute is missing how to implement this
This is normally implemented in two steps:
Implement a validation mechanism for the method that handles the incoming request. Normally you would throw an exception here if the input is incorrect, in your example a missing JSON key.
Implement a global error handler that will process the exception from point 1 and format the response as JSON.
For point 1 the usual choice is the Java Bean Validation framework because it's integrated with Spring Boot and allows to define validation constraints with annotations like #NotEmpty. You can take a look at this example.
For point 2 the usual choice is #RestControllerAdvice or #ControllerAdvice. You would have to understand your service web server setup to implement it properly e.g. it might behave differently if you use Spring WebFlux.
Can someone please help clarify and show how to properly go about this issue?
What I understand so far is that the Spring framework uses Message Converters when a method is annotated with #ResponseBody to convert the return Java object to a format that can be accepted by the Client. If the clients' HTTP request Accept Header includes "application/json", it will use Jackson and the Jackson converter to convert the object and return it in a json format. Similarly if the Accept Header includes "application/xml", then the Message Converter will use Jaxb and the corresponding converter to convert the object to xml.
Now my issue is that I include both the Jackson and Jaxb libraries as specified in Spring documentation so that the corresponding converters can work. This should be enough for Spring to employ #ResponseBody as its supposed to. However, when I send an HTTP Request with the Accept header "application/xml" I get a 406 status code and when I send one with "application/json" I receive a correct json response.
From my research online, I see that some people use the ContentNegotiation technique to work around this, but I would like to use the Message Converter for now. However, every technique to make the Message Converter technique to respond to json and xml resource requests involve formatting my POJO with JAXB annotation. Is this really necessary?
I guess what I am asking is how would one set up their project properly so that Spring can use the Message Converter technique to respond to json and xml requests? What libraries must be included? Does one need to add JAXB annotations or is there an automatic way for Spring to format an object into xml the way it does for json?
I thank you for your time and help with this, but so far I am really loving Spring's implementation of JAX-RS!
I want to build an Apache camel application to download a Jira
issue report, parse it, and store it into a .csv file.
I'm new at Apache camel, I do believe the jira here should be an endpoint, how to setup this configuration, I want to set is as from:("Jira") to (csv file).
I believe it could be something like this:
public void configure() throws Exception {
from("jira://pullRequestComment?ProjectKey=CAMEL-0000&IssueTypeId=1&IssueSummary=title")
.process(new MyLogProcessor())
.bean(new MyTransformer(),"transformContent")
.to("file:D:/camel/output");
}
I tried the above code, I got an exception for java conversion type.
Exception:
The JIRA component returns Java objects from the JIRA REST API. You need to either:
Support passing in the object type to your processor class as a method argument
Convert the JIRA Java Object to something else, then pass into your processor
BTW- The JIRA component caches "seen" data to know what is "new" to pass into the route. For really busy JIRA servers, this looks and acts like a memory leak so you'll need to be mindful to manage that scenario
The pullRequestComment endpoint is for a producer endpoint (i.e. it can only go in to("jira:pullRequestComment?..."). Since you want to poll for new comments, you should use the newComment endpoint. So your route would look something like:
from("jira:newComment?serverUrl={host}&username={username}password={password}")
.process(new MyLogProcessor())
.bean(new MyTransformer(),"transformContent")
.to("file:D:/camel/output");
Note that this endpoint returns an object of type com.atlassian.jira.rest.client.domain.Comment, so in MyLogProcessor if you do exchange.getIn().getBody(), it will return an object of type Comment (or maybe a List if there are multiple objects, you'll have to test this).
If you want to post a pull request comment, then you can use the pullRequestComment endpoint like the following:
from("direct://some/uri/name")
.header("ProjectKey", "CAMEL-0000")
.header("IssueTypeId", 1L)
.header("IssueSummary", "title")
.to("jira:pullRequestComment?serverUrl={host}&username={username}password={password}")
.... // More processing here
Then if you invoke the route from("direct://some/uri/name"), it should post the comment that's in the exchange body.
I want to have the following flow:
Request -> 1) Validate JSON Body -> 2) Validate JSON for Security Concerns -> ...etc
And Throw exceptions / give appropriate JSON Responses in 1) or 2).
I have tried to use Interceptors and Filters.
Filters: I can modify the body by having a Request Wrapper and then passing it along the chain to the JSON Validation, however when I throw exceptions they are not intercepted by the #ControllerAdvice Exception Handler, which works for everything else. From what I have read this is by design...? Also I have tried to set a response manually, example below, but it seems spring boot changes the status code to 405.
response.getWriter().write("{\"test\" : \"test\"");
response.sendError(400);
Interceptors: I get an error because I am reading the body more than once, I can not see how to set the Custom HttpServletRequestWrapper I have made before the interceptors run.
I am after a way of implementing this scenario.
Any help would be greatly appreciated. Thanks!
I realised that because Filters are run first I can modify the request by using a Request Wrapper. Then I use an interceptor to run the security and json checks.
I would still like to know how to modify responses in a Filter though, I do not like how spring boot will change it even though you already have a response code.
Consider a Person entity with a property name that is annotated as #NotNull. Then a simple PersonRepository and this repo exposed with Spring Data Rest.
When I POST to create a new Person, if the name property is null a ValidationException occurs as expected. But what I actually get on the client is an Internal Server Error (500) and the message is a TransactionSystemException that happened much later in the exception chain.
What I'd expect to get is a Bad Request (400) with the actual ValidationException and all it's useful information so the client can know what's wrong with the posted data.
There seems to be a way to attach custom validators with SDR as explained here. But the thing is, this is not a custom validator, it's a standard bean validation that happens when the repository is asked to save data. So I'm not really sure how those two come together.
So questions:
What are my options to let the client know what's wrong with the submitted data when using SDR?. Things like what fields are invalid and what's the error for each field would be awesome.
Are there any examples about this anywhere?
Thanks a lot.
What you need is a proper ExceptionHandler, it will handle back end exceptions and send meaningful rich messages (json/xml) to the front end client.
Take a look a this git repository