How solve the error "retrofit \n not found: limit=1 content=0d…" - retrofit

I am developing with Java on Android, using retrofit library, at some point it gives me the error
retrofit not found: limit = 1 content = 0d ...
and the connection is interrupted.
I am doing my tests at the moment with an emulator. Nexus_5_API_R_Android 10 emulator, API 29.
My Gradle:
defaultConfig {
applicationId "ni.iniser"
minSdkVersion 21
targetSdkVersion 29
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
Libraries:
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
implementation 'com.squareup.okhttp3:logging-interceptor:4.8.1'
The implementation
private static Retrofit crearRetrofit(String baseURL) {
Retrofit retrofit = null;
HttpLoggingInterceptor httpLoggingInterceptor = new HttpLoggingInterceptor()
.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.connectTimeout(5,TimeUnit.MINUTES)
.readTimeout(5,TimeUnit.MINUTES)
.writeTimeout(5,TimeUnit.MINUTES)
.addNetworkInterceptor(
new Interceptor() {
#NotNull
#Override
public Response intercept(#NotNull Chain chain) throws IOException {
Request original = chain.request();
Request request = original.newBuilder()
.header("Connection","close")
.build();
return chain.proceed(request);
}
}
)
.retryOnConnectionFailure(true)
.addInterceptor(httpLoggingInterceptor).build();
retrofit = new Retrofit.Builder()
.baseUrl(baseURL)
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create())
.build();
return retrofit;
}

Related

How to read HTTP 500 using a Spring RestTemplate client

A simple Spring Boot REST Controller
#PostMapping(path = "check-and-submit", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<MyOutput> checkAndSave(#RequestBody #Valid MyInput input, Errors errors){
ResponseEntity<MyOutput> result = null;
if (errors.hasErrors()) {
result = new ResponseEntity<>(MyOutput.buildErrorResponse(errors), HttpStatus.INTERNAL_SERVER_ERROR);
} else {
myDao.save(input.buildEntity());
result = new ResponseEntity<>(MyOutput.buildSuccessResponse(), HttpStatus.OK);
}
return result;
}
And the test class for it
public static void main(String[] args) {
MyInput dto = new MyInput();
// set properties
RestTemplate restTemplate = new RestTemplate();
MultiValueMap<String, String> headers = new LinkedMultiValueMap<String, String>();
headers.add("Content-Type", "application/json");
restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
HttpEntity<MyInput> request = new HttpEntity<MyInput>(dto, headers);
try {
ResponseEntity<MyOutput> result = restTemplate.postForEntity(URL, request, MyOutput.class);
System.out.println(result);
} catch(Exception e) {
e.printStackTrace();
}
}
For success scenario this works fine. But, for exception scenrio, i.e. HTTP 500 this fails
org.springframework.web.client.HttpServerErrorException: 500 null
at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:97)
As suggested in one of the posts, I created a error-handler that can successfully read the response
public class TestHandler extends DefaultResponseErrorHandler {
#Override
public void handleError(ClientHttpResponse response) throws IOException {
Scanner scanner = new Scanner(response.getBody());
String data = "";
while (scanner.hasNext())
data += scanner.next();
System.out.println(data);
scanner.close();
}
}
But how can I let RestTemplate read and deserialize the response JSON even in case of HTTP 500.
Before any other human-question-flagging-bot marks this as duplicate, here's a humble explanation on how this is different from the others.
All other questions address how to handle HTTP 500, at max read the response-body. This questions is directed at if it is possible to deserialize the response as JSON as well. Such functionality is well established in frameworks such as JBoss RESTEasy. Checking how same can be achieved in Spring.
This should work.
try {
ResponseEntity<MyOutput> result = restTemplate.postForEntity(URL, request, MyOutput.class);
} catch(HttpServerErrorException errorException) {
String responseBody = errorException.getResponseBodyAsString();
// You can use this string to create MyOutput pojo using ObjectMapper.
}

Retrofit timeout not changing despite being set to the OkHttp client

I have a singleton class for network connections. I want the timeout increased and have written the following code.
BizAnalystApiv2 provideBizAnalystApiV2(final ObjectMapper mapper) {
final HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
if (BuildConfig.DEBUG) {
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
} else {
logging.setLevel(HttpLoggingInterceptor.Level.NONE);
}
final OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(logging)
.addInterceptor(new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException {
Request original = chain.request();
Request request = original.newBuilder()
.header("instanceId", FirebaseInstanceId.getInstance().getId())
.build();
return chain.proceed(request);
}
})
.readTimeout(70, TimeUnit.SECONDS)
.build();
return new Retrofit.Builder()
.addConverterFactory(JacksonConverterFactory.create(mapper))
.client(client)
.baseUrl(BizAnalystServicev2.getServerUrl())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build()
.create(BizAnalystApiv2.class);
}
The timeout remains at 15 seconds which is the default timeout. Can you suggest what is the issue.
I am using okhttp(v 3.4.1) and retrofit (v 2.1.0)

Retrofit 2 & OKHttp 3 caching response not working in my case

From the answer given for this question Can Retrofit with OKHttp use cache data when offline i was able to come up with this, but the code seems not to cache. What could i be doing wrong?
This my okhttp client
long SIZE_OF_CACHE = 10 * 1024 * 1024; // 10 MB
Cache cache = new Cache(getDirectory(), SIZE_OF_CACHE);
if (cache == null) {
Toast.makeText(AppController.getInstance().getApplicationContext(), "could n0t set cache", Toast.LENGTH_SHORT).show();
}
client = new OkHttpClient
.Builder()
.addNetworkInterceptor(REWRITE_CACHE_CONTROL_INTERCEPTOR)
.cache(cache)
.build();
Add my network interceptor is as below:
private static final Interceptor REWRITE_CACHE_CONTROL_INTERCEPTOR = new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException {
Response originalResponse = chain.proceed(chain.request());
if (isConnected()) {
int maxAge = 60; // read from cache for 1 minute
return originalResponse.newBuilder()
.header("Cache-Control", "public, max-age=" + maxAge)
.build();
} else {
int maxStale = 60 * 60 * 24; // tolerate 1-day stale
return originalResponse.newBuilder()
.header("Cache-Control", "public, only-if-cached, max-stale=" + maxStale)
.build();
}
}
};
Am adding to retrofit like this:
public static Retrofit getClient() {
createCacheForOkHTTP();
if (retrofit == null) {
retrofit = new Retrofit.Builder()
.client(client)
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return retrofit;
}
//Add in my activity:
ApiInterface apiService =
ApiClient.getClient().create(ApiInterface.class);
Call<MovieResponse> call = apiService.getPopularMoviesDetails(ApiKey, page);
call.enqueue(new Callback<MovieResponse>() {
#Override
public void onResponse(Call<MovieResponse> call, Response<MovieResponse> response) {
progressBar.setVisibility(View.GONE);
mErrorView.setVisibility(View.GONE);
if (response.isSuccessful()) {
movies = response.body().getResults();
movieAdapter.setMovieList(movies);
mRecyclerView.setAdapter(movieAdapter);
} else {
Toast.makeText(getActivity(), "header" + response.headers() + "code" + response.code() + "errorbody" + response.errorBody() + "errorbody" + response.message(), Toast.LENGTH_SHORT).show();
}
}
#Override
public void onFailure(Call<MovieResponse> call, Throwable t) {
progressBar.setVisibility(View.GONE);
// Log error here since request failed
Log.e(TAG, t.toString());
mErrorView.setVisibility(View.VISIBLE);
}
});
//interface
#GET("movie/popular")
Call<MovieResponse> getPopularMoviesDetails(#Query("api_key") String apiKey, #Query("page") int page);
You shouldn’t rewrite responses from the server to facilitate caching. It’s better to ask your server’s administrators to include cache headers when they serve responses. That way the cache works for all clients – not just OkHttp.
That said, you’re an adult and you’re entitled to take technical shortcuts to avoid talking to your server team.
First you need to change your code to use both a network interceptor and an application interceptor. Why two? Well, when you’re making requests into the cache the network interceptors haven’t run yet. And when you’re writing responses into the cache the application interceptors haven’t run yet.
Your application interceptor will rewrite your request headers to include this when you prefer the cache, and to permit the cache to serve stale responses:
Cache-Control: only-if-cached, max-stale=86400
Your network interceptor will rewrite your server’s response headers to include this so that all responses are cached for 24 hours:
Cache-Control: max-age=86400
Not sure if you already fix this. I found a great/short solution hope it helps.
I create a CacheControlInterceptor. This has 24 hours of cache, if there you are offline and your response is less than 24 hours old you will get the cache response.
public class CacheControlInterceptor implements Interceptor {
private static final String CACHE_CONTROL_HEADER = "Cache-Control";
private static final String MAX_AGE_HEADER_VALUE = "public, max-age=";
private static final String MAX_STALE_HEADER_VALUE = "public, only-if-cached, max-stale=";
// Specifies the maximum amount of time a resource will be considered fresh.
private static final int MAX_AGE = 60;
// Indicates that the client is willing to accept a response that has exceeded its expiration time.
private static final int MAX_STALE = 60 * 60 * 24; // 24 hours cache.
#Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
if (WatchItApplication.hasNetwork()) {
request = request.newBuilder()
.header(CACHE_CONTROL_HEADER, MAX_AGE_HEADER_VALUE + MAX_AGE).build();
} else {
request = request.newBuilder()
.header(CACHE_CONTROL_HEADER, MAX_STALE_HEADER_VALUE + MAX_STALE).build();
}
return chain.proceed(request);
}
}
On this method I create the OKHttpCliente . As you notice I am adding the CacheControlInterceptor and cache.
private OkHttpClient createDefaultOkHttpClient() {
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BASIC);
return new OkHttpClient.Builder()
.retryOnConnectionFailure(true)
.connectTimeout(TIMEOUT_MILLIS, TIMEOUT_UNIT)
.readTimeout(TIMEOUT_MILLIS, TIMEOUT_UNIT)
.writeTimeout(TIMEOUT_MILLIS, TIMEOUT_UNIT)
.addNetworkInterceptor(new HeaderInterceptor())
.cache(new Cache(WatchItApplication.getInstance().getCacheDir(), 10 * 1024 * 1024))
.addInterceptor(new CacheControlInterceptor())
.addInterceptor(interceptor)
.build();
}
Finally retrofit:
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.client(createDefaultOkHttpClient())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
Let me know if that works.
I was able to resolve the issue.This is how i did it.
private static OkHttpClient getCacheClient(final Context context) {
Interceptor REWRITE_CACHE_CONTROL_INTERCEPTOR = new Interceptor() {
#Override
public okhttp3.Response intercept(Chain chain) throws IOException {
okhttp3.Response originalResponse = chain.proceed(chain.request());
if (isConnected()) {
// Internet available; read from cache for 0 day
// Why? Reduce server load, better UX
int maxAge = 0;
return originalResponse.newBuilder()
.header("Cache-Control", "public, max-age=" + maxAge)
.build();
} else {
// No internet; tolerate cached data for 1 week
int maxStale = 60 * 60 * 24 * 7;
return originalResponse.newBuilder()
.header("Cache-Control", "public, only-if-cached, max-stale=" + maxStale)
.build();
}
}
};
File httpCacheDirectory = new File(context.getCacheDir(), "cachedir");
int size = 5 * 1024 * 1024;
Cache cache = new Cache(httpCacheDirectory, size);
return new OkHttpClient.Builder()
.addNetworkInterceptor(REWRITE_CACHE_CONTROL_INTERCEPTOR)
.cache(cache)
.build();
}
And usage:
public static Retrofit getClient(final Context context) {
if (retrofit == null) {
retrofit = new Retrofit.Builder()
.client(getCacheClient(context))
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return retrofit;
}

java.lang.NoClassDefFoundError: Failed resolution of: Lokio/Buffer;

when i use retrofit .I got the exception java.lang.NoClassDefFoundError: Failed resolution of: Lokio/Buffer;i use okhttpclient in order to set header for retrofit.get userList is a post method,and i need to send body in request.
private void getUserList(int startIndex){
final JSONObject audienceObj = ProtocolHelper.getProtocolUtils(mContext).getUserlistJsonObj(mRoomData.mUid, mRoomData.mRoomId, startIndex);
OkHttpClient okClient = new OkHttpClient.Builder()
.addInterceptor(
new Interceptor() {
#Override
public okhttp3.Response intercept(Interceptor.Chain chain) throws IOException {
Request original = chain.request();
// Request customization: add request headers
Request.Builder requestBuilder = original.newBuilder()
.header("sessionId", CommonData.getUserInfo(mContext).sessionId);
Request request = requestBuilder.build();
return chain.proceed(request);
}
})
.build();
String baseUrl = ProtocolUtils.BASE_URL+"/";
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(baseUrl)
.addConverterFactory(GsonConverterFactory.create())
.client(okClient)
.build();
String audienceUrl = ProtocolHelper.getProtocolUtils(mContext).getProtocolUrl(ProtocolUtils.PROTOCOL_MSG_ID_MEMBER_LIST);
AudienceInterface audienceInterface = retrofit.create(AudienceInterface.class);
Call<String> call = audienceInterface.getAudienceList(audienceUrl,audienceObj);
call.enqueue(new Callback<String>() {
#Override
public void onResponse(Call<String> call, Response<String> response) {
Log.d(TAG, "onResponse");
}
#Override
public void onFailure(Call<String> call, Throwable t) {
Log.d(TAG, "onFailure"+t.getMessage());
}
});
}
public interface AudienceInterface {
#POST("{url}")
Call<String>getAudienceList(#Path("url") String url,#Body JSONObject boder);
}
the log t.getMessage is :java.lang.NoClassDefFoundError: Failed resolution of: Lokio/Buffer;
I solved it by adding:
implementation 'com.squareup.okio:okio:2.1.0'
in dependencies under build.gradle(Module: app).
Alright.~I found this error last time too.
By this:
NoClassDefFoundError: Failed resolution of: Lokio/Buffer
You might lost another jar lib--Okio.
You can download the jar file from github:
https://github.com/square/okio
And add this lib to your project.

spring security oauth2 usage with async requests

From what I can tell, the SpringSecurityFilter chain runs twice per request for #Async requests, because it runs on the inbound request thread, is passed to the async code which runs on a different thread, and then when it attempts to write to the response thread the SpringSecurityFilter chain runs again.
This is causing a problem near access_token expiry because I am using RemoteTokenServices, and what happens is the original request is authenticated, the service activity takes about a second, and then RemoteTokenServices is called again, at which point the access_token has expired, so the request returns a 401.
What is the recommended solution here? I have been unable to prevent the SecurityFilterChain from running the second time on the response thread. Am I doing something wrong, or is this expected behavior? I see the SecurityContext correctly passed thru to the #Async thread, but it is null in the response thread.
Is there a way to ensure the SecurityFilterChain only runs once per request? Or is the solution to accept the multiple filter calls per request and handle it with caching somehow?
I'm using spring-boot 1.3.3.RELEASE and spring-security-oauth2 2.0.9.RELEASE.
logs:
INFO [..nio-exec-1] [Caching...] loadAuthentication: 0bc97f92-9ebb-411f-9e8e-e7dc137aeffe
DEBUG [..nio-exec-1] [Caching...] Entering CachingRemoteTokenService auth: null
DEBUG [..nio-exec-1] [Audit...] AuditEvent [timestamp=Wed Mar 30 12:27:45 PDT 2016, principal=testClient, type=AUTHENTICATION_SUCCESS, data={details=remoteAddress=127.0.0.1, tokenType=BearertokenValue=<TOKEN>}]
INFO [..nio-exec-1] [Controller] Callable testing request received
DEBUG [MvcAsync1] [TaskService] taskBegin
DEBUG [MvcAsync1] [TaskService] Entering TaskService auth: org.springframework.security.oauth2.provider.OAuth2Authentication#47c78d1a: Principal: testClient; Credentials: [PROTECTED]; Authenticated: true; Details: remoteAddress=127.0.0.1, tokenType=BearertokenValue=<TOKEN>; Granted Authorities: ROLE_CLIENT
DEBUG [MvcAsync1] [TaskService] end of task
INFO [..nio-exec-2] [Caching...] loadAuthentication: 0bc97f92-9ebb-411f-9e8e-e7dc137aeffe
DEBUG [..nio-exec-2] [Caching...] Entering CachingRemoteTokenService auth: null
DEBUG [..nio-exec-2] [RemoteTokenServices] check_token returned error: invalid_token
DEBUG [..nio-exec-2] [Audit...] AuditEvent [timestamp=Wed Mar 30 12:27:47 PDT 2016, principal=access-token, type=AUTHENTICATION_FAILURE, data={type=org.springframework.security.authentication.BadCredentialsException, message=0bc97f92-9ebb-411f-9e8e-e7dc137aeffe}]
relevant code:
controller:
#RequestMapping(value = "/callable",
method = RequestMethod.GET,
produces = { MediaType.APPLICATION_JSON_VALUE })
public #ApiResponseObject Callable<ApiResponse> runCallable(HttpServletRequest httpServletRequest)
throws InterruptedException {
log.info(String.format("Callable testing request received"));
Callable<ApiResponse> rv = taskService::execute;
return rv;
}
async service:
#Override
public ApiResponse execute() {
log.debug("taskBegin");
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
log.debug("Entering TaskService auth: " + auth);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
ApiResponse rv = new ApiResponse();
rv.setStatus(HttpStatus.OK.value());
log.debug("end of task");
return rv;
}
RemoteTokenServices implementation (note caching is commented out):
public class CachingRemoteTokenService extends RemoteTokenServices {
private static Log log = LogFactory.getLog(CachingRemoteTokenService.class);
#Override
//#Cacheable(cacheNames="tokens", key="#root.methodName + #accessToken")
public OAuth2Authentication loadAuthentication(String accessToken)
throws org.springframework.security.core.AuthenticationException,
InvalidTokenException {
log.info("loadAuthentication: " + accessToken);
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
log.debug("Entering CachingRemoteTokenService auth: " + auth);
return super.loadAuthentication(accessToken);
}
#Override
//#Cacheable(cacheNames="tokens", key="#root.methodName + #accessToken")
public OAuth2AccessToken readAccessToken(String accessToken) {
log.info("readAccessToken: " + accessToken);
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
log.debug("Entering CachingRemoteTokenService auth: " + auth);
return super.readAccessToken(accessToken);
}
}
and finally my security config:
#Configuration
public class Oauth2ResourceConfig {
private static Log log = LogFactory.getLog(Oauth2ResourceConfig.class);
#Value("${client.secret}")
private String clientSecret;
#Value("${check.token.endpoint}")
private String checkTokenEndpoint;
#Bean
#Lazy
public ResourceServerTokenServices tokenService() {
CachingRemoteTokenService tokenServices = new CachingRemoteTokenService();
tokenServices.setClientId("test-service");
tokenServices.setClientSecret(clientSecret);
tokenServices.setCheckTokenEndpointUrl(checkTokenEndpoint);
return tokenServices;
}
#Configuration
#EnableResourceServer
protected static class ResourceServerConfig extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/health-check").permitAll()
.antMatchers("/**").access("#oauth2.isClient() and #oauth2.hasScope('trust')");
#Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.resourceId("test-service");
}
}
}
got an answer here: https://github.com/spring-projects/spring-security-oauth/issues/736
apparently the fix is to configure security.filter-dispatcher-types=REQUEST, ERROR

Resources