I am trying to set the 'connectionTimeout' for a Camel CXF-RS component here which produces a RESTful requests on a 3rd party service. The default 30000 miliseconds is to long.
Exchange exchange = template.send("cxfrs://" + url, new Processor() {
public void process(Exchange exchange) throws Exception {
exchange.setPattern(ExchangePattern.InOut);
Message inMessage = exchange.getIn();
setupDestinationURL(inMessage);
// using the http central client API
inMessage.setHeader(CxfConstants.CAMEL_CXF_RS_USING_HTTP_API, Boolean.TRUE);
// set the Http method
inMessage.setHeader(Exchange.HTTP_METHOD, "PUT");
// set the relative path
inMessage.setHeader(Exchange.HTTP_PATH, url);
// Specify the response class , cxfrs will use InputStream as the response object type
inMessage.setHeader(CxfConstants.CAMEL_CXF_RS_RESPONSE_CLASS, Customer.class);
// set a customer header
inMessage.setHeader("key", "value");
// since we use the Get method, so we don't need to set the message body
inMessage.setBody(null);
}
});
I have tried adding this to our application-context as many have suggested, but cannot see it modifying the default values when debugging through the HTTPConduit and HTTPClientPolicy classes:
<http-conf:conduit name="*.http-conduit">
<http-conf:client ConnectionTimeout="5000"/>
</http-conf:conduit>
and I have tried appending
"?httpClientAPI=true&connectionTimeout=5000"
as options to the url string.
Any help or guidance would be much appreciated.
Adding the http-conf:conduit element in application-context as you did is the way to go and should work. What makes you say it does not?
Quite often a backend server take too long to answer, after the connection is made; in this case setting ReceiveTimeout is as important as ConnectionTimeout.
This is a sample camel Route which consumes RS requests and calls a third-party RS server; the ReceiveTimeout and ConnectionTimeout parameters work as expected.
<cxf:rsServer id="rsFrontServer" address="..." serviceClass="..."/>
<cxf:rsClient id="rsBackendClient" address=".../" serviceClass="..."/>
<http-conf:conduit name="*.http-conduit">
<http-conf:client ReceiveTimeout="5000" ConnectionTimeout="5000"/>
</http-conf:conduit>
<camelContext xmlns="http://camel.apache.org/schema/spring">
<route id="front">
<from uri="cxfrs:bean:rsFrontServer"/>
<!-- do stuff -->
<to uri="cxfrs:bean:rsBackendClient"/>
</route>
</camelContext>
Related
I need to create a reverse proxy that takes incoming request and based on the content of the request body, route the request to specific URI.
This is for a routing micro service that acts like a reverse proxy and does routing based on some information from each request body. This means for each request I need to parse the request body and get the "username" field and then make a JDBC connection to fetch additional information from the database. Based on that information in database, it would finally redirect the request to the correct URI.
From what I have now, I have 2 blocking methods. The first one is the parsing for the request body, the other one is the JDBC connection to the database. I understand that I should not put any blocking calls inside the gateway filter. I just don't know what I should do in this case. I could have both operations running async but in the end I still need the information from database to do routing.
#Bean
public RouteLocator apiLocator(RouteLocatorBuilder builder, XmlMapper xmlMapper) {
return builder.routes()
.route(r -> r
.path("/test")
.and()
.readBody(String.class, s -> true) // Read the request body, data will be cached as cachedRequestBodyObject
.filters(f -> f.filter(new GatewayFilter() {
#Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
try {
// The following method is blocking and should not be put here
xmlMapper.readValue((String) exchange.getAttribute("cachedRequestBodyObject"), Map.class);
} catch (Exception e) {
//TODO
}
return chain.filter(exchange);
}
}))
.uri("http://localhost:8080"))
.build();
}
The above example only includes the blocking parsing as my request body is XML based. My IDE is warning me of having a blocking call there which I really appreciate.
Any help is greatly appreciated. Thank you everyone!
After some research, Mono.fromCallable seems to be a good fit. I then asked the same question directly under the github repo, it turns out that using a servlet app may be better. For anyone who is interested to see what I came up with, please take a look here https://github.com/spring-cloud/spring-cloud-gateway/issues/1229
I have Feign client setup with Hystrix and I am trying to log all the HTTP status codes that I get from my API calls into a database. So this means, if one of my calls give me a 201, I would want to log that into DB. If my call results in a failure, my fallback handler can obviously log that but I want to do the DB inserts in one place. Does feign have a way to get access to responses or some kind of general callback?
You have to provide custom decoder to get your response in ResponseEntity<Object>.
NotificationClient notificationClient = Feign.builder()
.encoder(new JacksonEncoder())
.decoder(customDecoder())
.target(Target.EmptyTarget.create(NotificationClient.class));
Here you define your custom decoder bean. You can define your own by implementing Decoder but I'm using spring decoder.
#Bean
public Decoder customDecoder() {
HttpMessageConverter jacksonConverter = new MappingJackson2HttpMessageConverter(customObjectMapper());
ObjectFactory<HttpMessageConverters> objectFactory = () -> new HttpMessageConverters(jacksonConverter);
return new ResponseEntityDecoder(new SpringDecoder(objectFactory));
}
Now collect your response in ResponseEntity<Object>
ResponseEntity<Object> response = notificationClient.notify();
int status = response.getStatusCodeValue();
Another option is to create your own feign.Logger implementation, overriding the logAndRebufferResponse method:
protected Response logAndRebufferResponse(
String configKey, Level logLevel, Response response, long elapsedTime);
This may be simpler than creating a Decoder and is guaranteed to be called when a response is received regardless of status. Decoders are only called if the request does not trigger an error.
I have an MVC application talking to REST interface designed using CXF. I am submitting a POST request with dynamic size list as FormParam. I have seen very weird behavior. When list size exceeds certain limit, list in FormParam is always empty.
Is there a size limitation how big list can be sent in POST request. Below is my REST interface.
#POST
#Path("/addIds")
public void addIds(
#FormParam("newStatus") Status newStatus,
#FormParam("Ids") List<Long> Ids
);
Client is connecting to this interface using below configuration.
<jaxrs:client id="remoteInterfaceClient"
serviceClass="com.test.RemoteInterface"
username="test"
password="test"
address="${url}">
<jaxrs:features>
<bean class="org.apache.cxf.transport.common.gzip.GZIPFeature" />
<cxf:logging />
</jaxrs:features>
<jaxrs:providers>
<bean class=".......JacksonJaxbJsonProvider" />
</jaxrs:providers>
</jaxrs:client>
Any thoughts or suggestions where can I find relevant material to fix this.
This appears to be issue with size of data sent. I changed the REST API to below format and got it resolved. Since it is a POST API, default param type is Form Param, so need to explicitly mention them, Just had to mention newStatus as QueryParam since I am sending it in url.
#POST
#Path("/addIds")
public void addIds(
#QueryParam("newStatus") Status newStatus, List<Long> Ids
);
I use Apache Camel like a smart HTTP proxy, in front of REST APIs. I have a configuration file with routes to configure and it works great.
To avoid complexity, I will summerize the code by :
camelContext.addRoutes(new RouteBuilder(){
#Override
public void configure() throws Exception {
from("servlet:///v1.3?matchOnUriPrefix=true")
.to("http4://localhost:8080/my-rest-api-v1.3.0?bridgeEndpoint=true&throwExceptionOnFailure=false");
from("servlet:///v1.2?matchOnUriPrefix=true")
.to("http4://localhost:8080/my-rest-api-v1.2.1?bridgeEndpoint=true&throwExceptionOnFailure=false");
}
});
My problem is on the endpoint server. When I retrieve the Request URL from my HttpServletRequest, it gives me a "http://localhost:8080/my-rest-api-v1.3.0/resources/companies/" instead of "http://my.site.com/my-rest-api" (which is the URL of my proxy).
How can I transfer requested host name to my endpoint?
I don't find how to do it with Apache Camel.
HTTP request has properies (like 'host') in its Header and to use this property in camel you need just replace localhost:8080 with ${header.host} and use recipientList EIP (so you can use Simple language to create an URI):
camelContext.addRoutes(new RouteBuilder(){
#Override
public void configure() throws Exception {
from("servlet:///v1.3?matchOnUriPrefix=true")
.recipientList(simple("http4://${header.host}/my-rest-api-v1.3.0?bridgeEndpoint=true&throwExceptionOnFailure=false"));
from("servlet:///v1.2?matchOnUriPrefix=true")
.recipientList(simple("http4://${header.host}/my-rest-api-v1.2.1?bridgeEndpoint=true&throwExceptionOnFailure=false"));
}
});
UPDATED: I updated the code above according to the next link: http://camel.apache.org/how-do-i-use-dynamic-uri-in-to.html (to use dynamic uri you have to use recipient List EIP).
I'm building a very simple REST API using Jersey, and I've got a warning in my log files that I'm not sure about.
WARNING: A servlet POST request, to
the URI
http://myserver/mycontext/myapi/users/12345?action=delete,
contains form parameters in the
request body but the request body has
been consumed by the servlet or a
servlet filter accessing the request
parameters. Only resource methods
using #FormParam will work as
expected. Resource methods consuming
the request body by other means will
not work as expected.
My webapp only has the Jersey servlet defined, mapped to /myapi/*
How can I stop these warnings?
For me the warning was showing for POST application/x-www-form-urlencoded. And I am using Spring Boot which has an HiddenHttpMethodFilter that does a getParameter before anything else... So I ended up doing this nasty override:
#Bean
public HiddenHttpMethodFilter hiddenHttpMethodFilter() {
return new HiddenHttpMethodFilter() {
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
if ("POST".equals(request.getMethod())
&& request.getContentType().equals(MediaType.APPLICATION_FORM_URLENCODED_VALUE)) {
filterChain.doFilter(request, response);
} else {
super.doFilterInternal(request, response, filterChain);
}
}
};
}
This message is meant to warn developers about the fact that the request entity body has been consumed, thus any other attempts to read the message body will fail.
It is safe to ignore the message or filter it out from the logs:
java.util.logging.Logger jerseyLogger =
java.util.logging.Logger.getLogger(WebComponent.class.getName());
jerseyLogger.setFilter(new Filter() {
#Override
public boolean isLoggable(LogRecord record) {
boolean isLoggable = true;
if (record.getMessage().contains("Only resource methods using #FormParam")) {
isLoggable = false;
}
return isLoggable;
}
});
The following thread describes the warning you are receiving. It sounds as though you might have a filter defined in your web.xml that is processing the request before Jersey does.
Finally got rid of this by making sure I had Content-Type: application/json in my request headers (obviously, on the client side)
I just had my ajax-function in JQuery set to contentType: "application/x-www-form-urlencoded; charset=UTF-8" because with a prior solution (without Jersey) I had some encoding problems. When I removed that the message was gone and everything worked fine.
This warning is the only thing the WebComponent logs, so just turn logging up to ERROR level or turn off logging for this component in your logback.xml or wherever you have logging configured. You don't need to write a custom filter to ignore this specific message since there are no other messages logged from this component.
Source code snippet from org.glassfish.jersey.servlet.WebComponent version 2.14:
if(!form.asMap().isEmpty()) {
containerRequest.setProperty("jersey.config.server.representation.decoded.form", form);
if(LOGGER.isLoggable(Level.WARNING)) {
LOGGER.log(Level.WARNING, LocalizationMessages.FORM_PARAM_CONSUMED(containerRequest.getRequestUri()));
}
}
The localized message that is used for this warning message is:
form.param.consumed=A servlet request to the URI {0} contains form parameters in the request body but the request body has been consumed by the servlet or a servlet filter accessing the request parameters. Only resource methods using #FormParam will work as expected. Resource methods consuming the request body by other means will not work as expected.
Turn logging off for the WebComponent in your logback.xml like so:
<logger name="org.glassfish.jersey.servlet.WebComponent" level="OFF" additivity="false"/>
Right.
So I've been suffering this issue, and I've been trying to solve it on different ways, but I did't want to change my web.xml settings, just because if I was testing my application with Postman it worked perfect, but when it was being integrated with the webapp it fails with the mentioned issue (A servlet request to the URI {MY_URI} contains form parameters in the request body but the request body has been consumed by the servlet or a servlet filter accessing the request parameters. Only resource methods using #FormParam will work as expected. Resource methods consuming the request body by other means will not work as expected.)
So as #clijk mentioned, you only have to set your headers as:
"Content-Type":"application/json"
"charset":"UTF-8"
and voilá, the warning it's gone.
Thanks
In my case I've fixed this error when I've changed the Object Date to String in the method.
Error:
#POST
#Path("/myPath")
#Produces(MediaType.APPLICATION_JSON)
public List<MyObject> myMethod(#FormParam("StartDate") Date date) throws Exception {
Fixed
#POST
#Path("/myPath")
#Produces(MediaType.APPLICATION_JSON)
public List<MyObject> myMethod(#FormParam("StartDate") String date) throws Exception {
Put this to your resource signature. Or find this string in your project someone already use this if #PUT or #POST is used. This should help
import javax.ws.rs.Consumes;
#Consumes(MediaType.APPLICATION_JSON)