How create NTLM Authentification with Retrofit - java

Since 23 sdk Android class were excluded classes:
org.apache.http.auth.AuthScheme;
org.apache.http.auth.AuthSchemeFactory;
org.apache.http.impl.auth.NTLMScheme;
org.apache.http.impl.auth.NTLMEngine;
org.apache.http.impl.auth.NTLMEngineException;
As it is now authorized in AD, with login and password through a retrofit? There OKHttpklient can be through headers?

1) add package org.apache.httpcomponents:httpclient:4.5 to build.gradle (app)
//noinspection DuplicatePlatformClasses
implementation '**org.apache.httpcomponents:httpclient:4.5**'
2) Add package org.apache.http.impl.auth to your project (folder in /java)
3) Create public class in added org.apache.http.impl.auth package
public class PublicNTLMEngineImpl implements NTLMEngine {
// with content of http://svn.apache.org/repos/asf/httpcomponents/httpclient/tags/4.5.2/httpclient/src/main/java/org/apache/http/impl/auth/NTLMEngineImpl.java
}
4) use Giohji's NTLMAuthenticator with instance of new PublicNTLMEngineImpl
OkHttpClient httpClient = new OkHttpClient.Builder()
.connectTimeout(30, TimeUnit.SECONDS)
.writeTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.authenticator(new NTLMAuthenticator(username, password, domainOrComputerName))
.build();
5) source code:
http://svn.apache.org/repos/asf/httpcomponents/httpclient/tags/4.5.2/httpclient/src/main/java/org/apache/http/impl/auth/

I found the answer on okhttp's github.
It was posted by SelvinPL.
First you have to implement the NTLM authenticator (it uses NTLMEngineImpl, a standalone version of org.apache.http.impl.auth.NTLMEngineImpl, which was also created by SelvinPL). The code below is a slightly modified version of SelvinPL's implementation to run on the latest retrofit's version (2.1.0).
private static class NTLMAuthenticator implements Authenticator {
final NTLMEngineImpl engine = new NTLMEngineImpl();
private final String domain;
private final String username;
private final String password;
private final String ntlmMsg1;
private NTLMAuthenticator(String username, String password, String domain) {
this.domain = domain;
this.username = username;
this.password = password;
String localNtlmMsg1 = null;
try {
localNtlmMsg1 = engine.generateType1Msg(null, null);
} catch (Exception e) {
e.printStackTrace();
}
ntlmMsg1 = localNtlmMsg1;
}
#Override
public Request authenticate(Route route, Response response) throws IOException {
final List<String> WWWAuthenticate = response.headers().values("WWW-Authenticate");
if (WWWAuthenticate.contains("NTLM")) {
return response.request().newBuilder().header("Authorization", "NTLM " + ntlmMsg1).build();
}
String ntlmMsg3 = null;
try {
ntlmMsg3 = engine.generateType3Msg(username, password, domain, "android-device", WWWAuthenticate.get(0).substring(5));
} catch (Exception e) {
e.printStackTrace();
}
return response.request().newBuilder().header("Authorization", "NTLM " + ntlmMsg3).build();
}
}
Then you can register the authenticator like this example:
OkHttpClient client = new OkHttpClient.Builder()
.authenticator(new NTLMAuthenticator(username, password, domain))
.build();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(getURL(context))
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.build();
return retrofit.create(Api.class);
It works for com.squareup.retrofit2:retrofit:2.1.0.

Related

JAVA getToken using AuthenticationContext - oauth2 (for Office 365)

I have to change the authentication for our web app (hybris project). What I need is a way to authenticate against an SMTP server (Office 356).
The problem is to get a token using "tenant id", "client_id" and "client_secret."
Using POSTMAN it does work properly (using the same credentials):
https://login.microsoftonline.com/3c111111-aed1-4dc2-ab93-f69fbef31111/oauth2/v2.0/token
But using java, it hast to look like this (or similar):
private static final String CLIENT_ID = "3c11111-aed1-4dc2-ab93-f69fbef31112";
private static final String CLIENT_SECRET = "f69fbef31112f69fbef31112f69fbef31112";
private static final String EWS_URL = "https://outlook.office365.com/EWS/Exchange.asmx";
private static final String RESOURCE = "https://graph.microsoft.com/.default";
private static final String TENANT_ID = "3c111111-aed1-4dc2-ab93-f69fbef31111";
private static final String AUTHORITY = "https://login.microsoftonline.com/" + TENANT_ID + "/oauth2/v2.0/authorize";
public String getAccessToken() {
AuthenticationResult result = null;
final ExecutorService service = Executors.newFixedThreadPool(1);
try {
final AuthenticationContext context = new AuthenticationContext(AUTHORITY, false, service);
final Future<AuthenticationResult> future = context.acquireToken(RESOURCE,
new ClientCredential(CLIENT_ID, CLIENT_SECRET), null);
result = future.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
log.error("Error", e);
} catch (MalformedURLException e) {
e.printStackTrace();
} finally {
service.shutdown();
}
String accessToken = null;
final String refreshToken;
// cache token and expiration
if (result != null) {
accessToken = result.getAccessToken();
refreshToken = result.getRefreshToken();
}
return accessToken;
}
}
When I deploy the code, I get a message:
java.util.concurrent.ExecutionException: com.microsoft.aad.adal4j.AuthenticationException: {"error_description":"AADSTS900023: Specified tenant identifier ...
It looks like the tenant id was wrong, but using POSTMAN, it does work. What am I doing wrong?

Request is not send without block()

I want to use this webflux client code to send POST requests with reply and without reply. I tried this code implementation:
public class RestClientBuilder {
private String token;
private String username;
private String password;
private URL gatewayUrl;
private SslContextBuilder sslContextBuilder;
public static RestClientBuilder builder() {
return new RestClientBuilder();
}
public RestClientBuilder token(String token) {
this.token = validateAndTrim(token, "Token");
return this;
}
public RestClientBuilder usernamePassword(String username, String password) {
this.username = validateAndTrim(username, "Username");
this.password = validateAndTrim(password, "Password");
return this;
}
private String validateAndTrim(String value, final String parameter) {
if (value == null || value.trim().isEmpty()) {
throw new IllegalArgumentException(parameter + " is empty");
}
return value.trim();
}
public RestClientBuilder gatewayUrl(String gatewayUrl) {
String urlSt = validateAndTrim(gatewayUrl, "Gateway URL");
try {
this.gatewayUrl = new URL(urlSt);
} catch (MalformedURLException e) {
throw new IllegalArgumentException("Malformed URL: " + urlSt, e);
}
return this;
}
public RestClientBuilder truststore(File truststoreFile) {
getSslContextBuilder().trustManager(truststoreFile);
return this;
}
public RestClientBuilder sslCertificate(File keyCertChainFile, File keyFile, String keyPassword) {
getSslContextBuilder().keyManager(keyCertChainFile, keyFile, keyPassword);
return this;
}
public RestClient build() throws SSLException {
SslContext sslContext = sslContextBuilder != null ? sslContextBuilder.build() : null;
return new RestClient(gatewayUrl.toString(), token, username, password, sslContext);
}
private SslContextBuilder getSslContextBuilder() {
if (sslContextBuilder == null) {
sslContextBuilder = SslContextBuilder.forClient();
}
return sslContextBuilder;
}
}
Implementation of the rest client:
public class RestClient {
private WebClient client;
private String gatewayUrl;
public RestClient(String gatewayUrl, String token, String username, String password, SslContext sslContext) {
this.gatewayUrl = gatewayUrl;
WebClient.Builder builder = WebClient.builder().baseUrl(gatewayUrl);
if (sslContext != null) {
HttpClient httpClient = HttpClient.create().secure(sslContextSpec -> sslContextSpec.sslContext(sslContext));
ClientHttpConnector httpConnector = new ReactorClientHttpConnector(httpClient);
builder.clientConnector(httpConnector);
}
if (username != null && password != null) {
builder.filter(basicAuthentication(username, password));
}
client = builder.build();
}
public Mono<Void> executeOnly(ReportRequest transaction) {
Mono<ReportRequest> transactionMono = Mono.just(transaction);
return client.post().uri(gatewayUrl)
.accept(MediaType.APPLICATION_XML)
.contentType(MediaType.APPLICATION_XML)
.body(transactionMono, ReportRequest.class)
.retrieve()
.bodyToMono(Void.class);
}
}
Make remote calls:
public class ReportingProcessor {
private String URL2 = "......";
public void collectEnvironmentData() throws JAXBException {
ReportRequest report = new ReportRequest();
report.setVersion("1.0");
RestClient client = null;
try {
client = RestClientBuilder.builder()
.gatewayUrl(URL2)
// .token(contract.getTerminal_token())
// .usernamePassword("user", "password")
// .truststore(new File("server.pem"))
// .sslCertificate(new File("client.pem"), new File("clientKey.p8"), "secret")
.build();
} catch (SSLException e) {
e.printStackTrace();
}
Mono<Void> result = client.executeOnly(report);
Void response = result.block();
}
When I remove Void response = result.block(); the request is not send. I Can't find why. Can you give me some advice how to make the client code working without using block().
Whenever you work with Spring-webflux you have to keep one thing in mind. i.e You don't have to break your chain. because it is necessary to, someone should call subscribe on your chain. as it works on RXJava specification.
if you break the chain then you have to call block() which not recommended.
you have to modify your code in the below manner.
Let's Consider you have a handler which is making a call to your collectEnvironmentData() method and your method is making a call to remote service.
public Mono<ServerResponse> handelerMethod(ServerRequest request){
return collectEnvironmentData().flatMap(aVoid -> ServerResponse.ok().build());
}
your method should be modified to
public Mono<Void> collectEnvironmentData() throws JAXBException {
ReportRequest report = new ReportRequest();
report.setVersion("1.0");
RestClient client = null;
try {
client = RestClientBuilder.builder()
.gatewayUrl(URL2)
// .token(contract.getTerminal_token())
// .usernamePassword("user", "password")
// .truststore(new File("server.pem"))
// .sslCertificate(new File("client.pem"), new File("clientKey.p8"),
//"secret").build();
} catch (SSLException e) {
e.printStackTrace();
}
return client.executeOnly(report);
}
Change your implementation in the above manner, hope it will work.
How I would implement your method is:
public Mono<Void> executeOnly(ReportRequest transaction) {
Mono<ReportRequest> transactionMono = Mono.just(transaction);
return client.post().uri(gatewayUrl)
.accept(MediaType.APPLICATION_XML)
.contentType(MediaType.APPLICATION_XML)
.body(transaction, ReportRequest.class)
.exchange()
.then();
}
And then I would use it as follows:
client.executeOnly(report).subscribe()
Change the method return type to Mono<Void> for end to end streaming.
public void collectEnvironmentData() throws JAXBException {
ReportRequest report = new ReportRequest();
report.setVersion("1.0");
RestClient client = null;
try {
client = RestClientBuilder.builder()
.gatewayUrl(URL2)
.build();
} catch (SSLException e) {
e.printStackTrace();
}
return client.executeOnly(report);
}
Or you can also subscribe the Mono
client.executeOnly(report).subscribe();

How to pass Header in Retrofit 2.1.0

Hi From last two I'm stuck with this retrofit any one please help me.I have tried so many method to pass header in retrofit could but i couldn't im using Retrofit 2.0.1
build.gradle
compile 'com.google.code.gson:gson:2.6.2'
compile 'com.squareup.retrofit2:retrofit:2.1.0'
compile 'com.squareup.okhttp3:logging-interceptor:3.3.1'
compile 'com.squareup.okhttp:okhttp:2.7.2'
compile 'com.squareup.retrofit2:converter-gson:2.1.0'
And in
ApiClientHeader.jav
import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
public class ApiClientHeader {
public static final String BASE_URL = "URL";
private static Retrofit retrofit = null;
public static Retrofit getClient(final String token) {
OkHttpClient okClient = new OkHttpClient.Builder()
.addInterceptor(
new Interceptor() {
#Override
public Response intercept(Interceptor.Chain chain) throws IOException {
Request original = chain.request();
// Request customization: add request headers
Request.Builder requestBuilder = original.newBuilder()
.header("Authorization", "Bearer " + token)
.method(original.method(), original.body());
Request request = requestBuilder.build();
return chain.proceed(request);
}
})
.build();
if (retrofit == null) {
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.client(okClient)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return retrofit;
}
}
And In my
ApiInterface.java
#GET("profile")
Call<JsonObject> getProfile();
And In My Activity i am just calling function
private void getProfileData()
{
Singleton single = new Singleton();
String auth = single.getAuthorization();
Log.d("===========>>>>>>",auth);
ApiInterface apiService =
ApiClientHeader.getClient(auth).create(ApiInterface.class);
//showProgress(true);
Call<JsonObject> profileResponse = apiService.getProfile();
profileResponse.enqueue(new Callback<JsonObject>() {
#Override
public void onResponse(Call<JsonObject> call, Response<JsonObject> response) {
//showProgress(false);
Log.d("============>"," Call Request " +String.valueOf(call.request().toString()));
Log.d("============>", " Response code " + String.valueOf(response.code()));
// Log.d("============>", " Response Body " + String.valueOf(response.body().toString()));
if(response.code() == HttpURLConnection.HTTP_OK)
{
}
}
#Override
public void onFailure(Call<JsonObject> call, Throwable t) {
//showProgress(false);
Log.d("============>"," Call Request " +String.valueOf(call.request().toString()));
Log.d("============>"," Call Request " +String.valueOf(call.request().headers()));
Log.d("=======>", "Failure" + t.getMessage());
}
});
}
Still I am getting 403 Invalid acces.
Here i have not used any POJO class to send or receive data. Please help me.
You have to pass your authentication string like this
#GET("profile")
Call<JsonObject> getProfile((#Header("Authorization") String authorization))
#Headers({
"Accept: application/vnd.github.v3.full+json",
"User-Agent: Retrofit-Sample-App"
})
#GET("users/{username}")
Call<User> getUser(#Path("username") String username);
more info on documentation
I found one of the best link for Token based authentication using Retrofit 1.9 + OkHttp 2.4
You can add NetworkInterceptor, I have used it in my demo like:
httpClient.addNetworkInterceptor(new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException {
Request originalRequest = chain.request();
// Add authorization header with updated authorization value to intercepted request
Request authorisedRequest = originalRequest.newBuilder()
.header("Authorization", AccountAuthenticator.getAccessTokenWithTokenType(mContext))
.build();
return chain.proceed(authorisedRequest);
}
});
Dependencies:
compile 'com.squareup.retrofit2:retrofit:2.1.0'
compile 'com.squareup.retrofit2:converter-gson:2.1.0'
compile 'com.google.code.gson:gson:2.8.0'
compile 'com.squareup.okhttp3:logging-interceptor:3.3.1'
Thank you.
Try below code with your class, It is working for me:
final RestAdapter restAdapter = new RestAdapter.Builder().setClient(client).setLogLevel(RestAdapter.LogLevel.FULL).setRequestInterceptor(new RequestInterceptor() {
#Override
public void intercept(RequestFacade requestFacade) {
requestFacade.addHeader("key", "value");
requestFacade.addHeader("X-Requested-With", "XMLHttpRequest");
}
}).setConverter(new GsonConverter(gson)).setEndpoint(context.getString(R.string.base_url)).build();
Edit Post :
public class RestClient {
// private static final String BASE_URL = "http://api.plumperfect.com";
private WebServicesInterface apiService;
private static RestClient instance;
public RestClient(Context context) {
instance = this;
final Gson gson = new GsonBuilder().registerTypeAdapterFactory(new ItemTypeAdapterFactory()).setDateFormat("yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'SSS'Z'").create();
// final int cacheSize = 10 * 1024 * 1024; // 10 MiB
// final File cacheDirectory = new File(context.getCacheDir().getAbsolutePath(), "HttpCache");
// final OkHttpClient client = new OkHttpClient();
// Cache cache;
// try {
// cache = new Cache(cacheDirectory, cacheSize);
// client.setCache(cache);
// } catch (IOException e) {
// e.printStackTrace();
// }
final OkHttpClient okHttpClient = new OkHttpClient();
final Client client = new OkClient(okHttpClient);
final RestAdapter restAdapter = new RestAdapter.Builder().setClient(client).setLogLevel(RestAdapter.LogLevel.FULL).setRequestInterceptor(new RequestInterceptor() {
#Override
public void intercept(RequestFacade requestFacade) {
requestFacade.addHeader("key", "value");
requestFacade.addHeader("X-Requested-With", "XMLHttpRequest");
}
}).setConverter(new GsonConverter(gson)).setEndpoint(context.getString(R.string.base_url)).build();
apiService = restAdapter.create(WebServicesInterface.class);
}
public WebServicesInterface getApiService() {
return apiService;
}
public static RestClient getInstance() {
return instance;
}
}
Secound Class :
public class ItemTypeAdapterFactory implements TypeAdapterFactory {
#Override
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
final TypeAdapter<T> delegate = gson.getDelegateAdapter(this, type);
final TypeAdapter<JsonElement> elementAdapter = gson.getAdapter(JsonElement.class);
return new TypeAdapter<T>() {
public void write(JsonWriter out, T value) throws IOException {
delegate.write(out, value);
}
public T read(JsonReader in) throws IOException {
JsonElement jsonElement = elementAdapter.read(in);
if (jsonElement.isJsonObject()) {
JsonObject jsonObject = jsonElement.getAsJsonObject();
if (jsonObject.has(Constants.DATA) && jsonObject.get(Constants.DATA).isJsonObject()) {
jsonElement = jsonObject.get(Constants.DATA);
}
}
return delegate.fromJsonTree(jsonElement);
}
}.nullSafe();
}
}

How can I make a proper request to a URL using async task and look for a specific element?

I've recently used AsyncTask into my android app and what this piece of code is doing, is making a get request to the provided url, and retrieving the song name from it. (The URL is a Spotify song). When I print out the response I am getting, it says
com.(projectname).(projectname).TRetrieve$HttpGetRequest#aed19ea
(TRetrieve is the name of the class that is doing this request)
How can I make the request and properly get the name of the song back?
This is the code for the class so far:
public class HttpGetRequest extends AsyncTask<String, Void, String> {
public static final String REQUEST_METHOD = "GET";
public static final int READ_TIMEOUT = 15000;
public static final int CONNECTION_TIMEOUT = 15000;
#Override
protected String doInBackground(String... params){
String stringUrl = "https://open.spotify.com/track/5Q41NLTmGbVPozwHKK7bk2";
String result;
String inputLine;
try {
URL myUrl = new URL(stringUrl);
HttpURLConnection connection = (HttpURLConnection)
myUrl.openConnection();
connection.setRequestMethod(REQUEST_METHOD);
connection.setReadTimeout(READ_TIMEOUT);
connection.setConnectTimeout(CONNECTION_TIMEOUT);
connection.connect();
InputStreamReader streamReader = new
InputStreamReader(connection.getInputStream());
BufferedReader reader = new BufferedReader(streamReader);
StringBuilder stringBuilder = new StringBuilder();
while ((inputLine = reader.readLine()) != null) {
stringBuilder.append(inputLine);
}
reader.close();
streamReader.close();
result = stringBuilder.toString();
linkLoc = result.indexOf(testString) + testString.length();
for (int i = linkLoc; i < result.indexOf("on Spotify"); i++) {
sname += result.charAt(i) + "";
}
}
catch(IOException e){
e.printStackTrace();
sname = "Error";
}
return sname;
}
protected void onPostExecute(String result){
super.onPostExecute(result);
}
Interacting with API using AsyncTask / HttpURLConnection is very old-school. I suggest yo to use modern way to do it, using RxJava / Retrofit / Gson
1). Adding necessary dependencies
Add next dependencies to your <root>/app/build.gradle:
compile 'com.squareup.okhttp3:okhttp:3.3.1'
compile ('com.squareup.retrofit2:retrofit:2.0.2')
compile 'com.squareup.retrofit2:converter-gson:2.0.2'
compile 'com.squareup.retrofit2:adapter-rxjava:2.0.2'
compile 'com.google.code.gson:gson:2.4'
compile 'io.reactivex:rxjava:1.0.13'
compile 'io.reactivex:rxandroid:0.25.0'
2). Creating HTTP client
2.1)* (This is optional, only if you need to send AUTH token in headers)
Create interceptor:
import com.betcade.sdk.util.AuthUtil;
import java.io.IOException;
import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;
public class DBQRequestInterceptor implements Interceptor {
public DBQRequestInterceptor(){
}
#Override
public Response intercept(Chain chain) throws IOException {
Request original = chain.request();
Request.Builder requestBuilder = original.newBuilder();
if(<if necessary to send a token>){
requestBuilder.addHeader(<TOKEN_HEADER_NAME>, <TOKEN>);
}
Request request = requestBuilder.build();
return chain.proceed(request);
}
}
2.2)* (Also, optional step, only if you want to log request/response)
Create logging interceptor:
HttpLoggingInterceptor interceptor1 = new HttpLoggingInterceptor();
interceptor1.setLevel(HttpLoggingInterceptor.Level.BODY);
2.3) Create HTTP client
OkHttpClient client = new OkHttpClient.Builder()
.writeTimeout(8, TimeUnit.SECONDS)
.readTimeout(8, TimeUnit.SECONDS)
.addInterceptor(interceptor) // interceptor from step 2.1
.addInterceptor(interceptor1) // interceptor from step 2.2
.build();
2.4). Create TrackResponse model:
import javax.annotation.Generated;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
public class TrackResponse {
#SerializedName("id")
#Expose
private String id;
#SerializedName("name")
#Expose
private String name;
/**
*
* #return
* The id
*/
public String getId() {
return id;
}
/**
*
* #param id
* The id
*/
public void setId(String id) {
this.id = id;
}
/**
*
* #return
* The name
*/
public String getName() {
return name;
}
/**
*
* #param name
* The name
*/
public void setName(String name) {
this.name = name;
}
}
2.5) Create interface ApiClient:
public interface ApiClient {
#GET("tracks/{id}")
Observable<TrackResponse> getTrackInfo(#Path("id") String trackId);
}
2.6). Creat instance of ApiClient:
Retrofit API = new Retrofit.Builder()
.client(client)
.baseUrl("https://api.spotify.com/v1/")
.addConverterFactory(GsonConverterFactory.create(new Gson()))
.addCallAdapterFactory(RxErrorHandlingCallAdapterFactory.create())
.build();
ApiClient client = API.create(ApiClient.class);
2.7). Getting the name:
client.getTrackInfo("5Q41NLTmGbVPozwHKK7bk2")
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<TrackResponse>() {
#Override
public void call(TrackResponse trackResponse) {
String name = track.getName();
}
}, new Action1<Throwable>() {
#Override
public void call(Throwable throwable) {
throwable.printStackTrace();
}
});

Retrofit - Okhttp client How to cache the response

I'm trying to cache the response of http calls done by Retrofit(v 1.9.0) with OkHttp(2.3.0). It always made the network calls if I try to make a call without internet then java.net.UnknownHostException.
RestClient
public class RestClient {
public static final String BASE_URL = "http://something.example.net/JSONService";
private com.ucc.application.rest.ApiService apiService;
public RestClient() {
Gson gson = new GsonBuilder()
.setDateFormat("yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'SSS'Z'")
.create();
RequestInterceptor requestInterceptor = new RequestInterceptor() {
#Override
public void intercept(RequestFacade request) {
request.addHeader("Accept", "application/json");
int maxAge = 60 * 60;
request.addHeader("Cache-Control", "public, max-age=" + maxAge);
}
};
RestAdapter restAdapter = new RestAdapter.Builder()
.setLogLevel(RestAdapter.LogLevel.FULL)
.setEndpoint(BASE_URL)
.setClient(new OkClient(OkHttpSingleTonClass.getOkHttpClient()))
.setConverter(new GsonConverter(gson))
.setRequestInterceptor(requestInterceptor)
.build();
apiService = restAdapter.create(com.ucc.application.rest.ApiService.class);
}
public com.ucc.application.rest.ApiService getApiService() {
return apiService;
}
}
OkHttpSingleTonClass
public class OkHttpSingleTonClass {
private static OkHttpClient okHttpClient;
private OkHttpSingleTonClass() {
}
public static OkHttpClient getOkHttpClient() {
if (okHttpClient == null) {
okHttpClient = new OkHttpClient();
createCacheForOkHTTP();
}
return okHttpClient;
}
private static void createCacheForOkHTTP() {
Cache cache = null;
cache = new Cache(getDirectory(), 1024 * 1024 * 10);
okHttpClient.setCache(cache);
}
public static File getDirectory() {
final File root = new File(Environment.getExternalStorageDirectory() + File.separator + "UCC" + File.separator);
root.mkdirs();
final String fname = UserUtil.CACHE_FILE_NAME;
final File sdImageMainDirectory = new File(root, fname);
return sdImageMainDirectory;
}
}
MyActivity
Request request = new Request.Builder()
.cacheControl(new CacheControl.Builder()
.onlyIfCached()
.maxAge(60 * 60, TimeUnit.SECONDS)
.build())
.url(RestClient.BASE_URL + Constants.GET_ABOUT_US_COLLECTION + "?userid=59e41b02-35ed-4962-8517-2668b5e8dae3&languageid=488d8f13-ef7d-4a3a-9516-0e0d24cbc720")
.build();
Log.d("url_request", RestClient.BASE_URL + Constants.GET_ABOUT_US_COLLECTION + "/?userid=10");
com.squareup.okhttp.Response forceCacheResponse = null;
try {
forceCacheResponse = OkHttpSingleTonClass.getOkHttpClient().newCall(request).execute();
} catch (IOException e) {
e.printStackTrace();
}
if (forceCacheResponse.code() != 504) {
// The resource was cached! Show it.
Log.d("From", "Local");
Toast.makeText(AboutUs.this, "Local", Toast.LENGTH_SHORT).show();
} else {
// The resource was not cached.
Log.d("From", "Network");
Toast.makeText(AboutUs.this, "Network", Toast.LENGTH_SHORT).show();
getAbouUsDetails();//This will use the Apiservice interface to hit the server.
}
I followed this. But I can't manage to work. Its simply hitting from the server. What am i doing wrong?
As per Retrofit 1.9.0 which uses OkClient does not have Caching support. We have to use OkHttpClient instance by Square OkHttpClient library.
You can compile by compile 'com.squareup.okhttp:okhttp:2.3.0'
Before everything retrofit caches by response headers like
Cache-Control:max-age=120,only-if-cached,max-stale
** 120 is seconds.
You can read more about headers here.
Caching headers are mostly instructed by server response. Try to implement cache headers in servers. If you don't have an option, yes retrofit has it.
private static final Interceptor REWRITE_CACHE_CONTROL_INTERCEPTOR = new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException {
Response originalResponse = chain.proceed(chain.request());
return originalResponse.newBuilder()
.header("Cache-Control", String.format("max-age=%d, only-if-cached, max-stale=%d", 120, 0))
.build();
}
};
Where to cache:
private static void createCacheForOkHTTP() {
Cache cache = null;
cache = new Cache(getDirectory(), 1024 * 1024 * 10);
okHttpClient.setCache(cache);
}
// returns the file to store cached details
private File getDirectory() {
return new File(“location”);
}
Add interceptor to the OkHttpClient instance:
okHttpClient.networkInterceptors().add(REWRITE_CACHE_CONTROL_INTERCEPTOR);
And finally add OkHttpClient to the RestAdapter:
RestAdapter.setClient(new OkClient(okHttpClient));
And you can go through this slide for more reference.

Categories

Resources