【问题标题】:Using OkHttp client via OKClient on Google App Engine throws a "java.lang.NoClassDefFoundError: java.net.ProxySelector" is a restricted class error在 Google App Engine 上通过 OKClient 使用 OkHttp 客户端会引发“java.lang.NoClassDefFoundError: java.net.ProxySelector”是受限类错误
【发布时间】: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

【问题讨论】:

    标签: google-app-engine retrofit okhttp


    【解决方案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 正在运行,那么您就可以开始了。
        猜你喜欢
        • 2012-08-25
        • 1970-01-01
        • 2019-03-08
        • 1970-01-01
        • 2013-10-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-10-05
        相关资源
        最近更新 更多