I am trying to use OKHTTP (version 2.4.0) along retrofit (1.9.0) on google app engine (1.9.22).
Here is the how i use it:
OkHttpClient okHttpClient = new OkHttpClient();
okHttpClient.setConnectTimeout(COMPOSER_MODULE_CONNECTION_TIMEOUT, TimeUnit.SECONDS);
okHttpClient.setReadTimeout(COMPOSER_MODULE_SOCKET_TIMEOUT, TimeUnit.SECONDS);
RestAdapter restAdapter = new RestAdapter.Builder()
.setLogLevel(RestAdapter.LogLevel.FULL)
.setConverter(new JacksonConverter())
.setEndpoint(ENDPOINT_PATH)
.setClient(new OkClient(okHttpClient))
.build();
This throws the following error:
java.lang.NoClassDefFoundError: java.net.ProxySelector is a restricted class. Please see the Google App Engine developer's guide for more details.
at com.google.apphosting.runtime.security.shared.stub.java.net.ProxySelector.<clinit>(ProxySelector.java)
at com.squareup.okhttp.OkHttpClient.copyWithDefaults(OkHttpClient.java:614)
at com.squareup.okhttp.Call.<init>(Call.java:50)
at com.squareup.okhttp.OkHttpClient.newCall(OkHttpClient.java:595)
at retrofit.client.OkClient.execute(OkClient.java:53)
I gather from the error that "java.net.ProxySelector" is not white-listed for use on google appengine.
Question 1)
Is it possible to use OKHTTP (version 2.4.0) along retrofit (1.9.0) on google app engine (1.9.22)? i.e, is there a work around for this error
if not,
Question 2)
Are there any other way to:
(a) use async HTTP calls with google appengine (with URLFetchService, for instance) ?
(b) set connection and socket timeouts for the client used from (a) ?
The links i have come across via search:
(1) Retrofit timeout configuration for clients
(2) Google App Engine URL Fetch Java API
You can use HttpUrlConnection with Retrofit2 to use it in Google APP Engine
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import javax.servlet.http.HttpServletResponse;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.Headers;
import okhttp3.MediaType;
import okhttp3.Protocol;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.ResponseBody;
import okio.BufferedSink;
import okio.BufferedSource;
import okio.Okio;
public class RetrofitCall implements Call {
Request request;
RetrofitCall(Request request) {
this.request = request;
}
@Override
public Request request() {
return request;
}
@Override
public Response execute() throws IOException {
URL url = request.url().url();
final HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setUseCaches(false);
connection.setDoOutput(true);
connection.setRequestMethod(request.method());
Headers headers = request.headers();
if (headers != null) {
for (int i = 0; i < headers.size(); i++) {
String name = headers.name(i);
connection.setRequestProperty(name, headers.get(name));
}
}
if (request.body() != null) {
BufferedSink outbuf;
outbuf = Okio.buffer(Okio.sink(connection.getOutputStream()));
request.body().writeTo(outbuf);
outbuf.close();
}
connection.connect();
final BufferedSource source = Okio.buffer(Okio.source(connection.getInputStream()));
if (connection.getResponseCode() != HttpServletResponse.SC_OK) {
throw new IOException("Fail to call " + " :: " + source.readUtf8());
}
Response response = new Response.Builder()
.code(connection.getResponseCode())
.message(connection.getResponseMessage())
.request(request)
.protocol(Protocol.HTTP_1_1)
.body(new ResponseBody() {
@Override
public MediaType contentType() {
return MediaType.parse(connection.getContentType());
}
@Override
public long contentLength() {
return connection.getContentLengthLong();
}
@Override
public BufferedSource source() {
return source;
}
})
.build();
return response;
}
@Override
public void enqueue(Callback responseCallback) {
}
@Override
public void cancel() {
}
@Override
public boolean isExecuted() {
return false;
}
@Override
public boolean isCanceled() {
return false;
}
public static class Factory implements Call.Factory {
@Override
public Call newCall(Request request) {
return new RetrofitCall(request);
}
}
}
You can use the following code snippet to run Retorifit2 with GAE limitations. It contains a lot of debugging stuffs free to remove in production and does not implement real async call.
okhttp3.Call.Factory gaeCallFactory = new okhttp3.Call.Factory() {
@Override
public okhttp3.Call newCall(final Request request) {
final URL url = request.url().url();
final String method = url.toString();
return new okhttp3.Call() {
@Override
public Request request() {
return request;
}
@Override
public Response execute() throws IOException {
final HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setUseCaches(false);
if (request.body() != null) {
//TODO ajust for different needs
connection.setRequestProperty("Content-Type", "application/json");
connection.setDoOutput(true);
BufferedSink outbuf;
ByteArrayOutputStream out = new ByteArrayOutputStream();
outbuf = Okio.buffer(Okio.sink(out));
request.body().writeTo(outbuf);
outbuf.close();
logger.info("Calling " + method + "\n" + new String(out.toByteArray()));
outbuf = Okio.buffer(Okio.sink(connection.getOutputStream()));
request.body().writeTo(outbuf);
outbuf.close();
} else {
logger.info("Calling " + method);
}
final BufferedSource source = Okio.buffer(Okio.source(connection.getInputStream()));
if (connection.getResponseCode() != HttpServletResponse.SC_OK) {
throw new IOException("Fail to call " + method + " :: " + source.readUtf8());
}
Response response = new Response.Builder()
.code(connection.getResponseCode())
.message(connection.getResponseMessage())
.request(request)
.protocol(Protocol.HTTP_1_1)
.body(new ResponseBody() {
@Override
public MediaType contentType() {
return MediaType.parse(connection.getContentType());
}
@Override
public long contentLength() {
return connection.getContentLengthLong();
}
@Override
public BufferedSource source() {
return source;
}
})
.build();
logger.info("Call response code: " + response.code() + " message: " + response.message());
return response;
}
@Override
public void enqueue(Callback responseCallback) {
try {
responseCallback.onResponse(this, execute());
} catch (IOException e) {
responseCallback.onFailure(this, e);
}
}
@Override
public void cancel() {
}
@Override
public boolean isExecuted() {
return false;
}
@Override
public boolean isCanceled() {
return false;
}
};
}
};
Retrofit retrofit = new Retrofit.Builder()
.callFactory(gaeCallFactory)
.addConverterFactory(GsonConverterFactory.create())
.baseUrl(ENDPOINT_URI)
.build();
You need to use the Appengine URLFetchClient instead of the OkHttpClient. Like this:
import retrofit.appengine.UrlFetchClient;
RestAdapter restAdapter = new RestAdapter.Builder()
.setLogLevel(RestAdapter.LogLevel.FULL)
.setConverter(new JacksonConverter())
.setEndpoint(ENDPOINT_PATH)
.setClient(new UrlFetchClient())
.build();
Please note this only works with Retrofit1, this will not work with Retrofit2 because it's coupled directly to OkHttp as explained by Jake Wharton here