Struggling to get SpringCloud Feign to work with external services - spring-cloud

Using Feign to access services that I register on Eureka is a breeze. I am trying to use Feign to access external services and struggling with the basics.
I am playing with a service on Bluemix however to simplify the problem at hand, I am using a simple service.
My Proxy appears as follows:
//#FeignClient(name = "country-service-client", url = "https://country.io")
#FeignClient(name = "another-country-service-client", url = "http://restcountries.eu/rest/v2/name/Australia")
public interface SimpleServiceProxy {
//This one works
#RequestMapping(method = RequestMethod.GET, value = "/names.json", produces = "application/json")
String getCountries();
//This one does not work... This is used in conjunction where the url in the Fiegn Client annotation reads as - http://restcountries.eu/rest/v2
#RequestMapping(method = RequestMethod.GET, value = "/name/{country}", produces = "application/json")
public String getCountryInfo(#PathVariable("country") String country);
//This one doesn't work either
//#RequestMapping(method = RequestMethod.GET, value = "/name/Australia", produces = "application/json")
public String getCountryInfoHardcodedWithinMethod();
}
//This works however I would want to pass parameters and path variables to the URL
#RequestMapping(method = RequestMethod.GET, produces = "application/json")
public String getCountryInfoHardcodedAtFeignClientAnnotation();
}
I have tried a few variants (see the code above) and the last one where the URL is hardcoded at the Feign Client annotation works. The others throw a TimeoutException.
java.util.concurrent.TimeoutException: null
at com.netflix.hystrix.AbstractCommand.handleTimeoutViaFallback(AbstractCommand.java:958) ~[hystrix-core-1.5.3.jar:1.5.3]
at com.netflix.hystrix.AbstractCommand.access$400(AbstractCommand.java:59) ~[hystrix-core-1.5.3.jar:1.5.3]
at com.netflix.hystrix.AbstractCommand$11.call(AbstractCommand.java:573) ~[hystrix-core-1.5.3.jar:1.5.3]
at com.netflix.hystrix.AbstractCommand$11.call(AbstractCommand.java:565) ~[hystrix-core-1.5.3.jar:1.5.3]
at rx.internal.operators.OperatorOnErrorResumeNextViaFunction$4.onError(OperatorOnErrorResumeNextViaFunction.java:139) ~[rxjava-1.1.5.jar:1.1.5]
at rx.internal.operators.OperatorDoOnEach$1.onError(OperatorDoOnEach.java:71) ~[rxjava-1.1.5.jar:1.1.5]
at rx.internal.operators.OperatorDoOnEach$1.onError(OperatorDoOnEach.java:71) ~[rxjava-1.1.5.jar:1.1.5]
at com.netflix.hystrix.AbstractCommand$HystrixObservableTimeoutOperator$1.run(AbstractCommand.java:1099) ~[hystrix-core-1.5.3.jar:1.5.3]
at com.netflix.hystrix.strategy.concurrency.HystrixContextRunnable$1.call(HystrixContextRunnable.java:41) ~[hystrix-core-1.5.3.jar:1.5.3]
at com.netflix.hystrix.strategy.concurrency.HystrixContextRunnable$1.call(HystrixContextRunnable.java:37) ~[hystrix-core-1.5.3.jar:1.5.3]
at com.netflix.hystrix.strategy.concurrency.HystrixContextRunnable.run(HystrixContextRunnable.java:57) ~[hystrix-core-1.5.3.jar:1.5.3]
at com.netflix.hystrix.AbstractCommand$HystrixObservableTimeoutOperator$2.tick(AbstractCommand.java:1116) ~[hystrix-core-1.5.3.jar:1.5.3]
at com.netflix.hystrix.util.HystrixTimer$1.run(HystrixTimer.java:99) ~[hystrix-core-1.5.3.jar:1.5.3]
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) ~[na:1.8.0_112]
at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:308) ~[na:1.8.0_112]
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$301(ScheduledThreadPoolExecutor.java:180) ~[na:1.8.0_112]
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:294) ~[na:1.8.0_112]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) ~[na:1.8.0_112]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) ~[na:1.8.0_112]
at java.lang.Thread.run(Thread.java:745) [na:1.8.0_112]
I am puzzled and trying to figure things out. I want to get the hardcoded methods working before trying to figure out why the PathVariables aren't working.
What am I missing? (or doing incorrectly here)?

I just tried this simple application and it worked fine for me using
Dalston.RC1
#SpringBootApplication
#EnableFeignClients
#RestController
public class DemoApplication {
#Autowired
SimpleServiceProxy proxy;
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
#RequestMapping("/")
public String getCountry() {
return proxy.getCountryInfo("Australia");
}
}
#FeignClient(name = "another-country-service-client", url ="http://restcountries.eu/rest/v2")
interface SimpleServiceProxy {
#RequestMapping(method = RequestMethod.GET, value = "/name/{country}", produces = "application/json")
public String getCountryInfo(#PathVariable("country") String country);
}
The exception in your question is indicating a timeout via Hystrix when making the request. You can try disabling Hystrix and seeing if it goes away using feign.hystrix.enabled=false.

Related

Rest Assured: Why do I get IllegalStateException exception?

I am in the process of studying Rest-Assured framework.
I am using http://ziptasticapi.com free API for my drills.
When I call:
final static String BASE_URI = "http://ziptasticapi.com/";
final static String ADAK_ZIP_CODE = "99546"; //{"country":"US","state":"AK","city":"ADAK"}
final static String ATKA_ZIP_CODE = "99547";
public static final String GET_METHOD = "GET";
RestAssured.baseURI = BASE_URI;
String responseString = when().get(ADAK_ZIP_CODE).then()
.statusCode(200)
.and()
.extract()
.asString();
System.out.println(responseString);
I get the following string:
{"country":"US","state":"AK","city":"ADAK"}
as responseString value.
When I am trying:
RestAssured.baseURI = BASE_URI;
ZipData zipdata = when().get(ADAK_ZIP_CODE).then()
.statusCode(200)
.and()
.extract()
.as(ZipData.class);
public class ZipData {
public String country;
public String state;
public String city;
}
I crash on :
java.lang.IllegalStateException: Cannot parse object because no
supported Content-Type was specified in response. Content-Type was
'text/html;charset=UTF-8'.
Why is that? Could it be the rest returns an Html and not Json? How do I handle this?
Thanks!
First of all, keep in mind that REST Assured is a HTTP client primarily designed for testing HTTP APIs. So let me highlight that you shouldn't use REST Assured for anything other than testing.
Looks like the endpoint you are attempting to consume is returning a JSON document in the response payload, but the value of the Content-Type header is text/html;charset=UTF-8, so REST Assured cannot parse the response as a JSON document and convert it to an instance of ZipData. That's not what you expect from a sound HTTP API.
You could work around it and write a filter to override the Content-Type header, as show below:
public class OverrideContentTypeFilter implements Filter {
#Override
public Response filter(FilterableRequestSpecification requestSpec,
FilterableResponseSpecification responseSpec,
FilterContext ctx) {
Response next = ctx.next(requestSpec, responseSpec);
return new ResponseBuilder().clone(next).setContentType(ContentType.JSON).build();
}
}
Then use it as follows:
ZipData zipdata =
given()
.filter(new OverrideContentTypeFilter())
.when()
.get(uri)
.then()
.statusCode(200)
.extract()
.as(ZipData.class);

JUnit Test cases for Rest URL

I am facing problem to write test cases for a rest url which calls another URL through Rest Template.
Please find my code below:-
#RestController
public class ParentController{
#Value("${child.url}")
private String childUrl;
#Autowired
private RestTemplateUtil restTemplateUtil;
#RequestMapping(value = "/parent",method = RequestMethod.POST)
public ResponseEntity<Object> callChildController(#RequestBody InputParam inputParam, HttpServletRequest request) {
return restTemplateUtil.templateService(restTemplateUtil.formURL(request, childUrl), HttpMethod.POST,null,inputParam, Object.class);
}}
}
#Service
public class RestTemplateUtil {
RestTemplate restTemplate = new RestTemplate();
public ResponseEntity<Object> templateService(String url, HttpMethod method, HttpHeaders headers, ...............){
logger.info("Rest template service called..");
response = restTemplate.exchange(url,method,entity,responseType);
return response;
}
public String formURL(HttpServletRequest request, String childUrl){
return "http://" + request.getServerName() + ":" + request.getServerPort() + childUrl;
}
}
JUnit Test case written:-
Mockito.when(restTemplateUtil.templateService(Mockito.anyString(),
Mockito.<HttpMethod> eq(HttpMethod.POST),
Mockito.<HttpHeaders> any(),
Mockito.<HttpEntity<?>> any(),
Mockito.<Class<Object>> any())).thenReturn(mockRespEntity);
this.mvc.perform(post("/parent")
.contentType(MediaType.APPLICATION_JSON)
.content(new ObjectMapper().writeValueAsString(requestObj)))
.andExpect(status().is2xxSuccessful());
I am a newbie to Mockito, so with my meagre knowledge have build the above test case.
Please advise and correct me if I am wrong.
On executing this, I am getting error:-
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.springframework.web.client.ResourceAccessException: I/O error on POST request for "http://localhost:80/child": Connection refused: connect; nested exception is java.net.ConnectException: Connection refused: connect
The portno is wrong here.
Please help.
Thanks.

Invoking Camel Rest services gives me 401 using restlet

I am trying write a route to call a restful services. all of them donot have a body but query parameters. when i call(https) the rest service i get 401(unauthorised).
but if i use simple non-ssl (http) and invoke it works fine on other apis.
here is my Route and producer template.
Route
public static final String MONITOR_URI = "https://lsapi.thomson-pharma.com//ls-api-ws/ws/rs/opportunity-v1/match?drug=bevacizumab&company=Genentech Inc&fmt=json";
public static final String DIRECT_MONITOR = "direct:getDrugInfo";
from(DIRECT_MONITOR).to(MONITOR_URI).convertBodyTo(String.class);
=========================Main Class===============================
public static void main(String[] args) throws Exception {
CamelContext context = createCamelContext();
context.start();
final String text = "paracetamol";
final String fmt = "json";
final String authMethod = "Digest";
final String authUsername = "TR_Internal_024";
final String authPassword="ZTYA5S1KLF7WCDMN";
final String query = String.format("text=%s&fmt=%s&authMethod=%s&authUsername=%s&authPassword=%s",text,fmt,authMethod,authUsername,authPassword);
Map<String,Object> headers = new HashMap<String, Object>(){
{
put(Exchange.HTTP_METHOD,"POST");
put(Exchange.AUTHENTICATION,"Digest");
put("authUsername","TR_Internal_024");
put("authPassword","ZTYA5S1KLF7WCDMN");
put(Exchange.HTTP_QUERY,query);
}
};
ProducerTemplate template = context.createProducerTemplate();
String request = template.requestBodyAndHeaders(Constants.DIRECT_MONITOR,null,headers,String.class);
System.out.println("Body is : "+request);
}
Can someone help how to configure SSL using camel cxf or restlet ?
How do i add Credentials Provider to CamelContext or Spring Context ?
APologies for the delay. i got it worked by retriving the component from camelContext below is the code.
=========================================================================
HttpComponent http = (HttpComponent) camelContext.getComponent("https");
HttpClientConfigurer httpClientConfigurer = http.getHttpClientConfigurer();
if(httpClientConfigurer == null){
System.out.println("httpClientConfigurer is null");
if(http.getHttpClientConfigurer() == null ){
HttpConfiguration httpConfiguration = new HttpConfiguration();
httpConfiguration.setAuthMethod(AuthMethod.Digest);
httpConfiguration.setAuthUsername("xxxxx");
httpConfiguration.setAuthPassword("xxxxxx");
http.setHttpConfiguration(httpConfiguration);
}
}
Regards
Ram

How to access http session object in jersey REST service?

Hi i want to access data to anf from http session object in rest service i have googled a lot and find that #context of javax.ws.rs.core.Context gives HttpServlet object but as it is interfaec i always get it null. my code is as following
#Context
private HttpServletRequest request;
private HttpSession session = request.getSession();
#Path(value = "/listAllQuestion")
#GET
#Produces(MediaType.APPLICATION_JSON)
public Response listAllQuestion(){
int pgNo = 1;
int pgSize = 5;
IPResponse response = new IPResponse();
try {
if (session.getAttribute(IpConstants.TOPIC) != null
&& session.getAttribute(IpConstants.LEVEL) != null) {
session.removeAttribute(IpConstants.TOPIC);
session.removeAttribute(IpConstants.LEVEL);
}
session.setAttribute(IpConstants.PAGENO, pgNo);
session.setAttribute(IpConstants.PAGESIZE, pgSize);
quesList = questionService.listAllQuestion(pgNo, pgSize);
please try this:
Move your request.getSession() down inside your listAllQuestion() method, as so:
public Response listAllQuestion(){
HttpSession session = request.getSession():
I don't believe your request object will be populated outside the context of an actual request handler.

Handling REST Exception

I'm using a REST service with CXF that does a GET request with an int parameter.
#Path("parkingservice")
public interface ParkingService {
/**
* #param id
* #return
*/
#GET
#Path("/parkings/{id}/parkingState")
#Produces(MediaType.APPLICATION_JSON)
ParkingState getStatus(#PathParam("id") int id);
}
I call my service this way :
http://domain.com:8087/services/rest/parkingservice/parkings/2805/parkingState
When I put a number, it works, but when I put a String, I have this exception :
WARNING: javax.ws.rs.NotFoundException: java.lang.NumberFormatException: For input string: "dd"
at org.apache.cxf.jaxrs.utils.InjectionUtils.handleParameter(InjectionUtils.java:322)
at org.apache.cxf.jaxrs.utils.InjectionUtils.createParameterObject(InjectionUtils.java:845)
at org.apache.cxf.jaxrs.utils.JAXRSUtils.readFromUriParam(JAXRSUtils.java:1041)
at org.apache.cxf.jaxrs.utils.JAXRSUtils.createHttpParameterValue(JAXRSUtils.java:733)
at org.apache.cxf.jaxrs.utils.JAXRSUtils.processParameter(JAXRSUtils.java:708)
at org.apache.cxf.jaxrs.utils.JAXRSUtils.processParameters(JAXRSUtils.java:655)
at org.apache.cxf.jaxrs.interceptor.JAXRSInInterceptor.processRequest(JAXRSInInterceptor.java:238)
at org.apache.cxf.jaxrs.interceptor.JAXRSInInterceptor.handleMessage(JAXRSInInterceptor.java:99)
at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:271)
at org.apache.cxf.transport.ChainInitiationObserver.onMessage(ChainInitiationObserver.java:121)
at org.apache.cxf.transport.http.AbstractHTTPDestination.invoke(AbstractHTTPDestination.java:239)
at org.apache.cxf.transport.servlet.ServletController.invokeDestination(ServletController.java:223)
at org.apache.cxf.transport.servlet.ServletController.invoke(ServletController.java:203)
at org.apache.cxf.transport.servlet.ServletController.invoke(ServletController.java:137)
at org.apache.cxf.transport.servlet.CXFNonSpringServlet.invoke(CXFNonSpringServlet.java:158)
at org.apache.cxf.transport.servlet.AbstractHTTPServlet.handleRequest(AbstractHTTPServlet.java:243)
at org.apache.cxf.transport.servlet.AbstractHTTPServlet.doGet(AbstractHTTPServlet.java:168)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:621)
at org.apache.cxf.transport.servlet.AbstractHTTPServlet.service(AbstractHTTPServlet.java:219)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:222)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:99)
at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:947)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1009)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:589)
at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:312)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
at java.lang.Thread.run(Thread.java:680)
Caused by: java.lang.NumberFormatException: For input string: "dd"
at java.lang.NumberFormatException.forInputString(NumberFormatException.java:48)
at java.lang.Integer.parseInt(Integer.java:449)
at java.lang.Integer.valueOf(Integer.java:554)
at org.apache.cxf.common.util.PrimitiveUtils.read(PrimitiveUtils.java:60)
at org.apache.cxf.jaxrs.utils.InjectionUtils.handleParameter(InjectionUtils.java:310)
... 34 more
I'd like to avoid having this exception no matter what type I give..or to find a way to handle this exception with a message like "please provide a good Id"
Thanks a lot
Add an exception mapper to your rest layer. Code goes something like this :
package com.cxf.util;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
public class CustomExceptionMapper implements
ExceptionMapper<NotFoundException> {
public CustomExceptionMapper () {
}
#Override
public Response toResponse(NotFoundException arg0) {
//your logic and return accordingly
return Response.serverError().build();
}
}
And you if you are using Spring to maintain your service beans and cxf configuration then add this mapper as a bean under jaxrs:providers tag, where jaxrs is this : xmlns:jaxrs="http://cxf.apache.org/jaxrs"
Whenever your application will throw notfoundexception it will go in this class.
Second option is that you can take this parameter as String then parse it in your try catch block. On NumberformatException return 400 status code. Since you have a custom POJO as return, you can take a object of HttpServletResponse from org.apache.cxf.jaxrs.ext.MessageContext and set status in it. Below is a dummy code:
public class RestServiceImpl implements RestService{
#Context
MessageContext context;
public CustomPojo getData(String id){
HttpServletResponse httpResponse = context.getHttpServletResponse();
try{
}catch(NumberFormatException e){
httpResponse.setStatusCode(400);
}
}
}
Use the below signature for getStatus method, this would work both ways.
ParkingState getStatus(#PathParam("id") String id);
Your getStatus(#PathParam("id") int id); takes integer as an argument and you are trying to pass String instead of int, that's why it's throwing an exception of NumberFormatException.
You have to handle it,or make a Check is the incoming value is int if not then so a message "please provide a valid input".