在 Google App Engine 上通过 OKClient 使用 OkHttp 客户端会引发“java.lang.NoClassDefFoundError: java.net.ProxySele
Posted
技术标签:
【中文标题】在 Google App Engine 上通过 OKClient 使用 OkHttp 客户端会引发“java.lang.NoClassDefFoundError: java.net.ProxySelector”是受限类错误【英文标题】:Using OkHttp client via OKClient on Google App Engine throws a "java.lang.NoClassDefFoundError: java.net.ProxySelector" is a restricted class error 【发布时间】:2015-09-03 21:29:16 【问题描述】:我正在尝试在谷歌应用引擎 (1.9.22) 上使用 OKHTTP(版本 2.4.0)和改造 (1.9.0)。
这是我的使用方法:
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();
这会引发以下错误:
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)
我从错误中得知“java.net.ProxySelector”未列入白名单,无法在 google appengine 上使用。
问题 1) 是否可以在谷歌应用引擎(1.9.22)上使用 OKHTTP(版本 2.4.0)和改造(1.9.0)?即,是否有解决此错误的方法
如果没有, 问题2) 有没有其他方法:
(a) use async HTTP calls with google appengine (with URLFetchService, for instance) ?
(b) set connection and socket timeouts for the client used from (a) ?
我通过搜索找到的链接: (1)Retrofit timeout configuration for clients (2)Google App Engine URL Fetch Java API
【问题讨论】:
【参考方案1】:您可以使用 HttpUrlConnection 和 Retrofit2 在 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);
【讨论】:
【参考方案2】:您可以使用以下代码 sn-p 运行具有 GAE 限制的 Retorifit2。它包含许多可以在生产中免费删除的调试内容,并且没有实现真正的异步调用。
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();
【讨论】:
【参考方案3】:您需要使用 Appengine URLFetchClient 而不是 OkHttpClient。像这样:
import retrofit.appengine.UrlFetchClient;
RestAdapter restAdapter = new RestAdapter.Builder()
.setLogLevel(RestAdapter.LogLevel.FULL)
.setConverter(new JacksonConverter())
.setEndpoint(ENDPOINT_PATH)
.setClient(new UrlFetchClient())
.build();
请注意,这仅适用于 Retrofit1,这不适用于 Retrofit2,因为它直接与 OkHttp 耦合,正如 Jake Wharton here 所解释的那样
【讨论】:
未来读者注意:URLFetchClient
在单元测试的情况下将不起作用。如果您正在测试它,而 appengine 正在运行,那么您就可以开始了。以上是关于在 Google App Engine 上通过 OKClient 使用 OkHttp 客户端会引发“java.lang.NoClassDefFoundError: java.net.ProxySele的主要内容,如果未能解决你的问题,请参考以下文章
Google App Engine 通过内部网络与 Compute Engine 通信
如何从 Python 中的 App Engine 在 Google BigQuery 上创建架构?
在 Google App Engine 上通过 OKClient 使用 OkHttp 客户端会引发“java.lang.NoClassDefFoundError: java.net.ProxySele
从Iphone本机客户端通过Google App Engine进行身份验证