I have an implementation of Hystrix Circuit Breaker and when testing I'm getting a Hystrix Runtime Exception with the error that The CircuitBreker timed-out and fallback failed. Do I need to increase a timeout on the CircutBreaker? Should it just trip the circuit breaker if the code times out?
My junit test is as follows:
#Test
public void test4(){
client = new DefaultHttpClient();
httpget = new HttpGet("http://www.google.com:81");
resp = new CircuitBreaker(client, "test4", httpget).execute();
//assertEquals(HttpStatus.SC_GATEWAY_TIMEOUT, resp.getStatusLine().getStatusCode());
System.out.println(resp.getStatusLine().getStatusCode());
}
My Class is just to run web gets/puts/etc using the CircuitBreaker in case of failure somehow. My class is as follows:
public class CircuitBreaker extends HystrixCommand<HttpResponse> {
private HttpClient client;
private HttpRequestBase req;
protected String key;
//Set up logger
private static final Logger logger = (Logger)LoggerFactory.getLogger(CircuitBreaker.class);
/*
* This method is a constructor and sets http client based on provides args.
* This version accepts user input Hystrix key.
*/
public CircuitBreaker (HttpClient client, String key, HttpRequestBase req, int threshold) {
super(HystrixCommandGroupKey.Factory.asKey(key));
this.client = client;
this.key = key;
this.req = req;
logger.info("Hystrix Circut Breaker with Hystrix key:" + key);
logger.setLevel(Level.DEBUG);
HystrixCommandProperties.Setter().withCircuitBreakerEnabled(true);
HystrixCommandProperties.Setter().withCircuitBreakerErrorThresholdPercentage(threshold);
//HystrixCommandProperties.Setter().withCircuitBreakerRequestVolumeThreshold(50);
}
/*
* This method is a constructor and sets http client based on provides args.
* This version uses the default threshold of 50% failures if one isn't provided.
*/
public CircuitBreaker (HttpClient client,String key, HttpRequestBase req){
this(client, key, req, 50);
}
/*
* This method runs the command and returns the response.
*/
#Override
protected HttpResponse run() throws Exception {
HttpResponse resp = null;
resp = client.execute(req);
if (resp != null)
logger.info("Request to " + req.getURI() + " succeeded!");
return resp;
}
/*
* Fallback method in in the event the circuit breaker is tripped.
* Overriding the default fallback implemented by Hystrix that just throws an exception.
* #see com.netflix.hystrix.HystrixCommand#getFallback()
*/
#Override
protected HttpResponse getFallback() {
//For later expansion as needed.
logger.error("Circuit Breaker has " + getExecutionEvents() + ". Reason: "+ getFailedExecutionException().getMessage());
return null;
}
}
You can try to increase the timeout on your CircuitBreaker and see what happens:
HystrixCommandProperties.Setter().withExecutionTimeoutInMilliseconds(5000)
Because according to the Hystrix Wiki, the default timeout of HystrixCommand is 1 second, and it might take more than 1 second for your HttpGet return something.
You shouldn't need to increase the timeout to make a simple get request to google. Try this.
public class HttpCommand extends HystrixCommand<HttpResponse> {
private final HttpClient client;
private final HttpRequestBase req;
public HttpCommand(HttpClient client, HttpRequestBase req) {
super(HystrixCommandGroupKey.Factory.asKey("HttpCommandGroup"));
this.client = client;
this.req = req;
}
#Override
protected HttpResponse run() throws Exception {
return client.execute(req);
}
}
And a simple test
#Test
public void executeCommandTest(){
HttpClient client = HttpClientBuilder.create().build();
HttpGet httpget = new HttpGet("http://www.google.com");
HttpResponse resp = new HttpCommand(client, httpget).execute();
assertEquals(HttpStatus.SC_OK, resp.getStatusLine().getStatusCode());
}
Related
I am learning Akka with Java. I have written a simple program with two actors.
My first actor ActorA is called with list containing 1000 strings. ActorA loops through the list and calls ActorB for each element.
ActorB makes a Http POST call to external service using the String parameter received from ActorA.
I am expecting that ActorB will successfully make 1000 Http POST calls and will receive equal number of responses. However ActorB is able to make POST request randomly between 80-120 times then it stops making POST calls.
I tried providing a custom dispatcher as HTTP POST call is a blocking operation but still no luck!!
Refer to code and configuration given below.
public class ActorA extends AbstractActor {
static public Props props() {
return Props.create(ActorA.class);
}
static public class IdWrapper {
List<String> ids;
public IdWrapper(List<String> ids) {
this.ids = ids;
}
}
#Override
public Receive createReceive() {
return receiveBuilder()
.match(IdWrapper.class, this::process)
.build();
}
private void process(IdWrapper msg) {
msg.ids.forEach(id -> {
context().actorSelection("actorB").tell(new MessageForB(id), ActorRef.noSender());
}
);
}
}
public class ActorB extends AbstractActor {
final Http http = Http.get(getContext().system());
final Materializer materializer = ActorMaterializer.create(context());
public static Props props() {
return Props.create(ActorB.class);
}
static public class MessageForB implements Serializable {
String id;
public MessageForB(String id) {
this.id = id;
}
}
#Override
public Receive createReceive() {
return receiveBuilder()
.match(MessageForB.class, this::process)
.build();
}
private void process(MessageForB messageForB) {
ExecutionContext ec = getContext().getSystem().dispatchers().lookup("my-blocking-dispatcher");
/**
* Get id from request
*/
String reqId = messageForB.id;
/**
* Prepare request
*/
XmlRequest requestEntity = getRequest(Stream.of(reqId).collect(Collectors.toList()));
String requestAsString = null;
try {
/**
* Create and configure JAXBMarshaller.
*/
JAXBContext jaxbContext = JAXBContext.newInstance(XmlRequest.class);
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
jaxbMarshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);
/**
* Convert request entity to string before making POST request.
*/
StringWriter sw = new StringWriter();
jaxbMarshaller.marshal(requestEntity, sw);
requestAsString = sw.toString();
} catch (JAXBException e) {
e.printStackTrace();
}
/**
* Create RequestEntity from request string.
*/
RequestEntity entity = HttpEntities.create(
MediaTypes.APPLICATION_XML.toContentType(HttpCharsets.ISO_8859_1),
requestAsString);
/**
* Create Http POST with necessary headers and call
*/
final CompletionStage<HttpResponse> responseFuture =
http.singleRequest(HttpRequest.POST("http://{hostname}:{port}/path")
.withEntity(entity));
responseFuture
.thenCompose(httpResponse -> {
/**
* Convert response into String
**/
final CompletionStage<String> res = Unmarshaller.entityToString().unmarshal
(httpResponse.entity(), ec, materializer);
/**
* Consume response bytes
**/
httpResponse.entity().getDataBytes().runWith(Sink.ignore(), materializer);
return res;
})
.thenAccept(s -> {
try {
/**
* Deserialize string to DTO.
*/
MyResponse MyResponse = getMyResponse(s);
// further processing..
} catch (JAXBException e) {
e.printStackTrace();
}
});
}
private XmlRequest getRequest(List<String> identifiers){
XmlRequest request = new XmlRequest();
// Business logic to create req entity
return request;
}
private MyResponse getMyResponse(String s) throws JAXBException {
JAXBContext jaxbContext = JAXBContext.newInstance
(MyResponse.class);
javax.xml.bind.Unmarshaller jaxbUnmarshaller = jaxbContext
.createUnmarshaller();
StringReader reader = new StringReader(s);
return (MyResponse)
jaxbUnmarshaller.unmarshal(reader);
}
}
my-blocking-dispatcher {
type = Dispatcher
executor = "thread-pool-executor"
thread-pool-executor {
core-pool-size-min = 5
core-pool-size-max = 20
}
throughput = 1
}
Where can I improve or correct my code so that ActorB will successfully be able to make Http POST calls for all the items sent by ActorA ?
As i see you have used http.singleReques.
According to the akka-http docs
For these cases Akka HTTP offers the Http().singleRequest(...) method, which simply turns an HttpRequest instance into Future[HttpResponse]. Internally the request is dispatched across the (cached) host connection pool for the request’s effective URI.
http.singleRequest uses connection pool to handle requests so you need to increase number of connections in connection pool from akka http config.
in host-connection-pool section with this defaults:
host-connection-pool {
max-connections = 4
min-connections = 0
max-retries = 5
max-open-requests = 32
pipelining-limit = 1
idle-timeout = 30 s
}
Solution 2 :
using http.outgoingConnection
according to the akka-http docs it will be create an specific connection per request. So you can handle 1000 connections in parallel without connection pool.
With the connection-level API you open a new HTTP connection to a target endpoint by materializing a Flow returned by the Http().outgoingConnection(...) method. Here is an example:
def run(req:String): Unit ={
val apiBaseUrl = "example.com" //without protocol
val path = "/api/update"
val body = HttpEntity(ContentTypes.`application/json`,req.getBytes)
val request = HttpRequest(HttpMethods.POST, path,entity = body)
val connectionFlow = Http().outgoingConnection(apiBaseUrl)
val result = Source.single(request).via(connectionFlow).runWith(Sink.head)
result.onComplete{
case Success(value) =>
println(value)
case Failure(e)=>
e.printStackTrace()
}
}
I have a fairly simple case where I am trying to add HTTP headers (not SOAP headers) to a request I am making using Spring's WebServiceTemplate.
I have defined a ClientInterceptor where I am doing:
#Override
public boolean handleRequest(MessageContext messageContext) throws WebServiceClientException {
try {
TransportContext context = TransportContextHolder.getTransportContext();
HttpComponentsConnection connection = (HttpComponentsConnection) context.getConnection();
connection.addRequestHeader("Authorization", String.format("Bearer %s", someAccessToken));
} catch (IOException exception) {
// Do nothing
}
return true;
}
This is how I configure my SomeClient which extends WebServiceConfigurationSupport:
#Bean
public SomeClient someClient() {
...
SomeClientImpl service = new SomeClientImpl();
service.setObjectFactory(new com.path.ObjectFactory());
service.setDefaultUri(someUri);
service.setMarshaller(marshaller);
service.setUnmarshaller(marshaller);
service.setxStreamMarshaller(xStreamMarshaller);
service.setInterceptors(new ClientInterceptor[]{wss4jSecurityInterceptor()});
service.setMessageSender(new HttpComponentsMessageSender());
service.setInterceptors(new ClientInterceptor[]{wss4jSecurityInterceptor(), addHttpHeaderInterceptor()});
return service;
}
#Bean
public ClientInterceptor addHttpHeaderInterceptor() {
return new AddHttpHeaderInterceptor(someAccessToken);
}
#Bean
public Wss4jSecurityInterceptor wss4jSecurityInterceptor() {
Wss4jSecurityInterceptor interceptor = new Wss4jSecurityInterceptor();
interceptor.setSecurementActions(securementAction);
interceptor.setSecurementUsername(securementUsername);
interceptor.setSecurementPassword(securementPassword);
interceptor.setSecurementPasswordType(WSConstants.PW_TEXT);
interceptor.setSecurementMustUnderstand(false);
return interceptor;
}
But the Authorization header is not being added. I have also tried with a CustomMessageCallback:
public class CustomMessageCallback implements WebServiceMessageCallback {
private String headerKey;
private String headerValue;
public CustomMessageCallback(String headerKey, String headerValue) {
this.headerKey = headerKey;
this.headerValue = headerValue;
}
#Override
public void doWithMessage(WebServiceMessage webServiceMessage) throws IOException, TransformerException {
TransportContext context = TransportContextHolder.getTransportContext();
HttpComponentsConnection conn = (HttpComponentsConnection) context.getConnection();
HttpPost post = conn.getHttpPost();
post.addHeader(headerKey, headerValue);
}
}
But it does not seem to work as well. What am I doing wrong, why the Authorization header is not being added? Thanks!
Use the HeadersAwareSenderWebServiceConnection interface instead of the actual underlying connection.
TransportContext context = TransportContextHolder.getTransportContext();
HeadersAwareSenderWebServiceConnection connection = (HeadersAwareSenderWebServiceConnection) context.getConnection();
connection.addRequestHeader("Authorization", String.format("Bearer %s", "********"));
Now if you upgrade/switch HTTP library you don't need to change this code.
To answer your question about what you are doing wrong is that you are casting to the wrong class. Yes the class you are using is deprecated but it is part of the library you are using, you cannot just cast to a different class without changing the underlying HTTP library.
What I did in past is to use a WebServiceMessageCallback like this one:
public class WsHttpHeaderCallback implements WebServiceMessageCallback
{
private String headerKey;
private String headerValue;
private String soapAction;
public WsHttpHeaderCallback(String headerKey, String headerValue, String soapAction)
{
super();
this.headerKey = headerKey;
this.headerValue = headerValue;
this.soapAction = soapAction;
validateRequiredFields();
}
public WsHttpHeaderCallback()
{
super();
}
#Override
public void doWithMessage(WebServiceMessage message) throws IOException, TransformerException
{
validateRequiredFields();
addRequestHeader(headerKey, headerValue);
if (StringUtils.hasText(this.soapAction))
{
AxiomSoapMessage axiomMessage = (AxiomSoapMessage) message;
axiomMessage.setSoapAction(this.soapAction);
}
}
private void validateRequiredFields()
{
if( !StringUtils.hasText(headerKey) )
{
throw new IllegalArgumentException("Impossibile proseguire. Passato HEADER HTTP con chiave non valida: ["+headerKey+"]");
}
if( !StringUtils.hasText(headerValue) )
{
throw new IllegalArgumentException("Impossibile proseguire. Passato HEADER HTTP con valore non valido: ["+headerValue+"]");
}
}
private void addRequestHeader(String headerKey, String headerValue)
{
TransportContext context = TransportContextHolder.getTransportContext();
WebServiceConnection connection = context.getConnection();
if (connection instanceof HttpComponentsConnection)
{
HttpComponentsConnection conn = (HttpComponentsConnection) connection;
HttpPost post = conn.getHttpPost();
post.addHeader(headerKey, headerValue);
}
else if( connection instanceof ClientHttpRequestConnection )
{
ClientHttpRequestConnection conn = (ClientHttpRequestConnection)connection;
conn.getClientHttpRequest().getHeaders().add(headerKey, headerValue);
}
}
}
Then I used it in this way:
wsTemplate.marshalSendAndReceive(wsUrl, request, new WsHttpHeaderCallback(headerKey, headerValue, soapAction) );
In this way I successfully set all the needed HttpHeaders (in my case just one :) )
I hope it is usefull
Angelo
TL;DR
Your messageSender should be an instance of HttpComponentsMessageSender instead of HttpUrlConnectionMessageSender. Also you need to provide proper credentials.
getConnection() function of TransportContext returns an implementation of WebServiceConnection. Both HttpUrlConnection and HttpComponentsConnection are implementations of the same. So basically you are getting the wrong type of connection,hence the ClassCastException.
The ClientInterceptor will work for custom headers but not for Authorization header. For that, your HttpComponentsMessageSender needs to be configured with your credentials.
The proper configuration should be like this
#Value("${username}")
private String username;
#Value("${password}")
private String password;
#Bean
public SomeClient someClient() {
SomeClientImpl service = new SomeClientImpl();
service.setMessageSender();
//other configs
return service;
}
public HttpComponentsMessageSender getMessageSender(){
HttpComponentsMessageSender httpComponentsMessageSender = new HttpComponentsMessageSender();
httpComponentsMessageSender.setCredentials(getCredentials);
}
public UsernamePasswordCredentials getCredentials(){
return new UsernamePasswordCredentials(username, password);
}
I went through a similar exercise, for an endpointInterceptor the connection returns a HttpServletConnection. Therefore used the following and managed to get the HTTP headers added.
HttpServletConnection connection = (HttpServletConnection)context.getConnection();
HttpServletResponse response = connection.getHttpServletResponse();
HttpServletRequest request = connection.getHttpServletRequest();
response.addHeader("myheader", "myvalue");
Some additional tips:
If you want to send back the same header you received in the request, use following in the handleResponse method of the endpointInterceptor
response.addHeader("myheader", request.getHeader("myheader"));
If you are trying to add custom headers in an clientInterceptor to send to a downstream use below in the handleRequest method,
HttpUrlConnection connection = (HttpUrlConnection)context.getConnection();
connection.addRequestHeader("myheader", "myvalue");
I have an app with a lot of existing Retrofit 1.9 interfaces. I'd like to begin
upgrading to Retrofit 2.x incrementally (all at once is not currently feasable)
to gain support for RxJava call adapters (and because 1.9 is no longer being developed).
It was fairly trivial to get Retrofit1's RestAdapter to share an OkHttp3 client
that would be used in the Retrofit2 interfaces. Version 1.9 and 2.x also have
different maven groupIds, so the classes can exist side by side with no issues.
However, I get the following exception at runtime:
java.lang.IllegalAccessError: Method 'com.google.gson.stream.JsonWriter com.google.gson.Gson.newJsonWriter(java.io.Writer)' is inaccessible to class 'retrofit2.converter.gson.GsonRequestBodyConverter'
Retrofit 1 has a hard dependency on GSON 2.3.1, The method in question was made public in GSON 2.4. I've set my Gradle dependencies so that the GSON dependency resolves to v2.7 (the latest version as I post this):
build.gradle
compile('com.squareup.retrofit:retrofit:1.9.0') {
exclude module: 'gson'
}
compile 'com.jakewharton.retrofit:retrofit1-okhttp3-client:1.1.0'
compile "com.squareup.retrofit2:retrofit:2.3.0"
compile "com.squareup.retrofit2:converter-gson:2.3.0"
compile "com.squareup.retrofit2:adapter-rxjava:2.3.0"
compile 'com.google.code.gson:gson:2.7'
Running ./gradlew :app:dependencies indicates that GSON 2.7 is being resolved, however the runtime behavior is suspicious...
Update: I found that a 3rd party hardware SDK was bundling GSON 2.3.1 inside it's AAR. I can't figure out how to remove it though.
I recently implemented Retrofit 2.9.0 alongside Retrofit 1.9 because of version 2's ability to handle sessions is much better and one of my API calls was failing because of the lack of handling response cookies(the session).
I have the same issue where migrating the entire project to Retrofit 2 is not feasible at this time. I can confirm that it is working though.
I will show you snippets of how I implemented both 1.9 and 2.9.0. See bottom for a link to the full class.
For both:
Create a class from which you can access your Retrofit object and call interfaces from:
public class ApiManager {
private static final String TAG = "API MANAGER";
private static final String API_URL = BuildConfig.API_URL;
private static Gson gson = new GsonBuilder()
.setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ")
.setLenient()
.create();
// The rest of the class to follow
and then specific to 1.9:
private static RequestInterceptor requestInterceptor = new RequestInterceptor() {
#Override
public void intercept(RequestInterceptor.RequestFacade request) {
SessionManager sessionManager = new SessionManager(ContextHandler.getContext());
HashMap session = sessionManager.getUserDetails();
Object session_id = session.get("session_id");
Object token = session.get("token");
if (session_id != null && token != null) {
request.addHeader("Cookie", "session_id=" + session_id + ";");
request.addHeader("Cookie", "token=" + token + ";");
Log.i("INTERCEPT", "Sent Cookies");
}
request.addHeader("Accept", "application/json");
}
};
public static OkHttpClient getClient() {
// init okhttp 3 logger
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
JavaNetCookieJar jncj = new JavaNetCookieJar(CookieHandler.getDefault());
OkHttpClient client = new OkHttpClient();
client.newBuilder()
.addInterceptor(new AddCookiesInterceptor(ContextHandler.getContext()))
.addInterceptor(new ReceivedCookiesInterceptor(ContextHandler.getContext()))
.addNetworkInterceptor(logging)
.cookieJar(jncj)
.connectTimeout(10, TimeUnit.SECONDS)
.writeTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.MINUTES);
return client;
}
private static final RestAdapter REST_ADAPTER = new RestAdapter.Builder()
.setEndpoint(API_URL) // On device
.setRequestInterceptor(requestInterceptor)
.setClient(new Ok3Client(getClient()))
.setConverter(new GsonConverter(gson))
.setLogLevel(RestAdapter.LogLevel.FULL) //log the request
.build();
public interface AuthenticationInterface {
#Headers("Content-type: application/json")
#POST("/auth/getsession")
void Authenticate(#Body Authentication Auth, Callback<SessionStore> response);
#Headers("Content-type: application/json")
#GET("/auth/logout")
void logout(Callback<String> response);
#Headers("Content-type: application/json")
#GET("/auth/logout")
String logout();
}
// Bind REST_ADAPTER to Interface
public static final AuthenticationInterface AUTHENTICATION_INTERFACE = REST_ADAPTER.create(AuthenticationInterface.class);
// Use this when you want to run the request.
public static AuthenticationInterface getAuthenticationService(){ return AUTHENTICATION_INTERFACE; }
So you would use the above as follows:
ApiManager.getAuthenticationService().Authenticate(auth, new Callback<SessionStore>() {
#Override
public void success(SessionStore sessionStore, Response response) {
// Do somthing
}
#Override
public void failure(RetrofitError error) {
// Handle Error
}
});
And for 2.9.0:
public static OkHttpClient getHeader() {
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient okClient = new OkHttpClient.Builder()
.addInterceptor(interceptor)
.addInterceptor(new AddCookiesInterceptor(ContextHandler.getContext()))
.addInterceptor(new ReceivedCookiesInterceptor(ContextHandler.getContext()))
.cookieJar(cookieJar)
.connectTimeout(10, TimeUnit.SECONDS)
.writeTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.MINUTES)
.addNetworkInterceptor(
new Interceptor() {
#Override
public Response intercept(Interceptor.Chain chain) throws IOException {
Request request = null;
Log.d("--Authorization-- ", "authorizationValue");
Request original = chain.request();
// Request customization: add request headers
Request.Builder requestBuilder = original.newBuilder();
SessionManager sessionManager = new SessionManager(ContextHandler.getContext());
HashMap session = sessionManager.getUserDetails();
Object session_id = session.get("session_id");
Object token = session.get("token");
if (session_id != null && token != null) {
requestBuilder.addHeader("Cookie", "session_id=" + session_id + ";");
requestBuilder.addHeader("Cookie", "token=" + token + ";");
Log.i("INTERCEPT", "Sent Cookies");
}
requestBuilder.addHeader("Accept", "application/json");
request = requestBuilder.build();
return chain.proceed(request);
}
})
.build();
return okClient;
}
private static final Retrofit REST_ADAPTER2 = new Retrofit.Builder()
.baseUrl(API_URL) // On device
.client(getHeader())
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
public interface JasperReportsInterface {
/**
*
* #param agent_id
* #param report_id
*/
#retrofit2.http.Headers("Content-type: application/json")
#retrofit2.http.GET("/agents/{agent_id}/reports/{report_id}/")
Call<Reports> GetAgentReportView(#retrofit2.http.Path("agent_id") String agent_id, #retrofit2.http.Path("report_id") String report_id);
/**
*
* #param agent_id
* #param report_id
*/
#retrofit2.http.Headers("Content-type: application/json")
#retrofit2.http.GET("/agents/{agent_id}/reports/{report_id}/jobs")
Call<Jobs> PollAgentReportData(#retrofit2.http.Path("agent_id") String agent_id, #retrofit2.http.Path("report_id") String report_id);
/**
*
* #param agent_id
* #param report_id
* #param jsonBody
*/
#retrofit2.http.Headers("Content-type: application/json")
#retrofit2.http.POST("/agents/{agent_id}/reports/{report_id}/jobs")
Call<String> PostAgentReportData(#retrofit2.http.Path("agent_id") String agent_id, #retrofit2.http.Path("report_id") String report_id, #retrofit2.http.Body JsonObject jsonBody);
/**
*
* #param agent_id
* #param report_id
* #param jsonBody
*/
#retrofit2.http.Headers("Content-type: application/json")
#retrofit2.http.POST("/agents/{agent_id}/reports/{report_id}/jobs")
Call<String> DownloadAgentReportData(#retrofit2.http.Path("agent_id") String agent_id, #retrofit2.http.Path("report_id") String report_id, #retrofit2.http.Body JsonObject jsonBody);
}
// Bind REST_ADAPTER2 to Interface
public static final JasperReportsInterface JASPER_REPORTS_INTERFACE = REST_ADAPTER2.create(JasperReportsInterface.class);
// Use this when you want to run the request.
public static JasperReportsInterface getJasperReportsService(){ return JASPER_REPORTS_INTERFACE; }
And you would use the above as follows:
Call<Reports> reportsCall = ApiManager.getJasperReportsService().GetAgentReportView(agentsID, reportTypeID);
reportsCall.enqueue(new retrofit2.Callback<Reports>() {
#Override
public void onResponse(Call<Reports> call, retrofit2.Response<Reports> response) {
if(response.isSuccessful()) {
report = response.body();
} else {
int statusCode = response.code();
// handle request errors yourself
ResponseBody errorBody = response.errorBody();
}
}
#Override
public void onFailure(Call<Reports> call, Throwable t) {
}
});
Dependencies you would need are the basic ones need for both 1.9 and 2 respectively.
See here for full class.
I would like to test okhttp's http2 function. And I make multi requsts to same host in async style. But, I found that, it involved multi connections, since the protocol is h2, It should use just one connection, right?
The code is below.
Ah, I'm using okhttp2.5
public class Performance {
private final OkHttpClient client = new OkHttpClient();
private final Dispatcher dispatcher = new Dispatcher();
private final int times = 20;
public Performance(){
dispatcher.setMaxRequestsPerHost(2);
client.setDispatcher(dispatcher);
// Configure the sslContext
// MySSLSocketFactory mySSLSocketFactory = new MySSLSocketFactory();
// client.setSslSocketFactory(mySSLSocketFactory);
// client.setHostnameVerifier(new HostnameVerifier() {
// public boolean verify(String s, SSLSession sslSession) {
// return true;
// }
// });
}
public void run()throws Exception{
for(int i=0; i<times; i++) {
Request request = new Request.Builder()
.url("https://http2bin.org/delay/1")
.build();
client.newCall(request).enqueue(new Callback() {
public void onFailure(Request request, IOException e) {
e.printStackTrace();
}
public void onResponse(Response response) throws IOException {
System.out.println(response.headers().get("OkHttp-Selected-Protocol"));
}
});
}
}
public static void main(String[] args)throws Exception{
Performance performance = new Performance();
performance.run();
}
}
There's a bug in OkHttp where multiple simultaneous requests each create their own socket connection, rather than coordinating a shared connection. This only happens when the connections are created simultaneously. Work around by yielding 500ms before the 2nd connection.
I need to retry request inside of OkHttp Interceptor. For example there is incoming request which needs Authorization token. If Authorization token is expired, server returns response with 403 code. In this case I am retrieving a new token and trying to make call again by using the same chain object.
But OkHttp throws an exception, which states that you cannot make two requests with the same chain object.
java.lang.IllegalStateException: network interceptor org.app.api.modules.ApplicationApiHeaders#559da2 must call proceed() exactly once
I wonder if there is a clean solution to this problem of retrying network request inside of OkHttp Interceptor?
public final class ApplicationApiHeaders implements Interceptor {
private static final String AUTHORIZATION = "Authorization";
private TokenProvider mProvider;
public ApplicationApiHeaders(TokenProvider provider) {
mProvider = provider;
}
#Override
public Response intercept(Chain chain) throws IOException {
Token token = mProvider.getApplicationToken();
String bearerToken = "Bearer " + token.getAccessToken();
System.out.println("Token: " + bearerToken);
Request request = chain.request();
request = request.newBuilder()
.addHeader(AUTHORIZATION, bearerToken)
.build();
Response response = chain.proceed(request);
if (!response.isSuccessful() && isForbidden(response.code())) {
Token freshToken = mProvider.invalidateAppTokenAndGetNew();
String freshBearerToken = freshToken.getAccessToken();
Request newRequest = chain.request();
newRequest = newRequest.newBuilder()
.addHeader(AUTHORIZATION, freshBearerToken)
.build();
response = chain.proceed(newRequest);
}
return response;
}
private static boolean isForbidden(int code) {
return code == HttpURLConnection.HTTP_FORBIDDEN;
}
}
Use .interceptors() instead of .networkInterceptors() which are allowed to call .proceed() more than once.
For more information see: https://square.github.io/okhttp/interceptors/