Ошибка загрузки нескольких файлов в Box с ошибкой клиента HTTP "соединение все еще установлено"

Я использую Box Java SDK v3.0.5 (последняя версия). В моем приложении я реализую синхронизацию с удаленным пользователем Box, и когда пользователь создает несколько файлов локально, я должен создать их на стороне Box.

Мой клиент создан по следующему коду:

this.client = new BoxClient(key, clientSecret, hub, parser, config);

С загрузкой одного файла все работает хорошо. Но когда несколько файлов представлены один за другим в одном потоке и используют этот код:

BoxFileUploadRequestObject obj = BoxFileUploadRequestObject.uploadFileRequestObject(parentId,                                                              name,data);
  obj.setLocalFileCreatedAt(created.getTime());
  obj.put("created_at", formatDate(created));
  return client.getFilesManager().uploadFile(obj);

Я сталкиваюсь со следующим исключением в моем приложении:

java.lang.IllegalStateException: Invalid use of SingleClientConnManager: connection still allocated.
Make sure to release the connection before allocating another one.
    at org.apache.http.impl.conn.SingleClientConnManager.getConnection(SingleClientConnManager.java:216) ~[httpclient-4.1.2.jar:4.1.2]
    at org.apache.http.impl.conn.SingleClientConnManager$1.getConnection(SingleClientConnManager.java:190) ~[httpclient-4.1.2.jar:4.1.2]
    at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:401) ~[httpclient-4.1.2.jar:4.1.2]
    at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:820) ~[httpclient-4.1.2.jar:4.1.2]
    at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:754) ~[httpclient-4.1.2.jar:4.1.2]
    at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:732) ~[httpclient-4.1.2.jar:4.1.2]
    at com.box.boxjavalibv2.BoxRESTClient.getResponse(BoxRESTClient.java:148) ~[boxjavalibv2-3.0.5.jar:na]
    at com.box.boxjavalibv2.BoxRESTClient.execute(BoxRESTClient.java:98) ~[boxjavalibv2-3.0.5.jar:na]
    at com.box.boxjavalibv2.BoxRESTClient.execute(BoxRESTClient.java:72) ~[boxjavalibv2-3.0.5.jar:na]
    at com.box.boxjavalibv2.resourcemanagers.AbstractBoxResourceManager.getResponseAndParse(AbstractBoxResourceManager.java:118) ~[boxjavalibv2-3.0.5.jar:na]
    at com.box.boxjavalibv2.filetransfer.BoxFileUpload.execute(BoxFileUpload.java:58) ~[boxjavalibv2-3.0.5.jar:na]
    at com.box.boxjavalibv2.resourcemanagers.BoxFilesManagerImpl.uploadFile(BoxFilesManagerImpl.java:134) ~[boxjavalibv2-3.0.5.jar:na]
    at org.exoplatform.clouddrive.box.BoxAPI.createFile(BoxAPI.java:745) ~[exo-clouddrive-services-core-1.1.0-SNAPSHOT.jar:1.1.0-SNAPSHOT]
.........

Наконец, только первый файл успешно загружен.

После поиска этой ошибки я обнаружил, что может помочь использование многопоточного соединения. Но так как этот код находится в Box SDK, единственное, что я смог попробовать, это использование BoxConnectionManager (который обеспечивает поточно- ориентированный режим) при создании клиента:

BoxConnectionManagerBuilder connManager = new BoxConnectionManagerBuilder();
this.client = new BoxClient(key, clientSecret, hub, parser, config, connManager.build());

Действительно, с диспетчером соединений, отправленным клиенту, возникает другая проблема, когда я пытаюсь получить доступ к службе Box API:

Caused by: com.box.restclientv2.exceptions.BoxRestException: null
at com.box.boxjavalibv2.BoxRESTClient.handleException(BoxRESTClient.java:183) ~[boxjavalibv2-3.0.5.jar:na]
at com.box.boxjavalibv2.BoxRESTClient.execute(BoxRESTClient.java:118) ~[boxjavalibv2-3.0.5.jar:na]
at com.box.boxjavalibv2.BoxRESTClient.execute(BoxRESTClient.java:72) ~[boxjavalibv2-3.0.5.jar:na]
at com.box.boxjavalibv2.resourcemanagers.AbstractBoxResourceManager.getResponseAndParse(AbstractBoxResourceManager.java:118) ~[boxjavalibv2-3.0.5.jar:na]
at com.box.boxjavalibv2.resourcemanagers.AbstractBoxResourceManager.getResponseAndParseAndTryCast(AbstractBoxResourceManager.java:108) ~[boxjavalibv2-3.0.5.jar:na]
at com.box.boxjavalibv2.resourcemanagers.BoxEventsManagerImpl.getEventOptions(BoxEventsManagerImpl.java:60) ~[boxjavalibv2-3.0.5.jar:na]
at org.exoplatform.clouddrive.box.BoxAPI.updateChangesLink(BoxAPI.java:671) ~[exo-clouddrive-services-core-1.1.0-SNAPSHOT.jar:1.1.0-SNAPSHOT]
... 76 common frames omitted
Caused by: javax.net.ssl.SSLException: hostname in certificate didn't match: <api.box.com/74.112.185.97> != <*.box.com> OR <*.box.com> OR <box.com>
at org.apache.http.conn.ssl.AbstractVerifier.verify(AbstractVerifier.java:228) ~[httpclient-4.1.2.jar:4.1.2]
at org.apache.http.conn.ssl.BrowserCompatHostnameVerifier.verify(BrowserCompatHostnameVerifier.java:54) ~[httpclient-4.1.2.jar:4.1.2]
at org.apache.http.conn.ssl.AbstractVerifier.verify(AbstractVerifier.java:149) ~[httpclient-4.1.2.jar:4.1.2]
at org.apache.http.conn.ssl.AbstractVerifier.verify(AbstractVerifier.java:130) ~[httpclient-4.1.2.jar:4.1.2]
at org.apache.http.conn.ssl.SSLSocketFactory.connectSocket(SSLSocketFactory.java:397) ~[httpclient-4.1.2.jar:4.1.2]
at org.apache.http.conn.ssl.SSLSocketFactory.connectSocket(SSLSocketFactory.java:495) ~[httpclient-4.1.2.jar:4.1.2]
at org.apache.http.conn.scheme.SchemeSocketFactoryAdaptor.connectSocket(SchemeSocketFactoryAdaptor.java:62) ~[httpclient-4.1.2.jar:4.1.2]
at org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.java:148) ~[httpclient-4.1.2.jar:4.1.2]
at org.apache.http.impl.conn.AbstractPoolEntry.open(AbstractPoolEntry.java:149) ~[httpclient-4.1.2.jar:4.1.2]
at org.apache.http.impl.conn.AbstractPooledConnAdapter.open(AbstractPooledConnAdapter.java:121) ~[httpclient-4.1.2.jar:4.1.2]
at org.apache.http.impl.client.DefaultRequestDirector.tryConnect(DefaultRequestDirector.java:573) ~[httpclient-4.1.2.jar:4.1.2]
at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:425) ~[httpclient-4.1.2.jar:4.1.2]
at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:820) ~[httpclient-4.1.2.jar:4.1.2]
at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:754) ~[httpclient-4.1.2.jar:4.1.2]
at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:732) ~[httpclient-4.1.2.jar:4.1.2]
at com.box.boxjavalibv2.BoxRESTClient.getResponse(BoxRESTClient.java:148) ~[boxjavalibv2-3.0.5.jar:na]
at com.box.boxjavalibv2.BoxRESTClient.execute(BoxRESTClient.java:98) ~[boxjavalibv2-3.0.5.jar:na]
... 81 common frames omitted

К сведению, в моей среде разработчиков я использую Tomcat на 8443 порту. Но с REST-клиентом по умолчанию (без менеджера соединений, размещенного в конструкторе клиента) все работало хорошо.

Какое решение я могу использовать для загрузки нескольких файлов в Box?

2 ответа

Решение

Я отвечаю на свой вопрос сам.

Первоначальная проблема "соединение еще выделено" была действительно актуальной, как предполагалось в вопросе выше. Коренной причиной была необходимость

Потокобезопасное соединение (для HTTP-клиента в Box SDK)... Но так как этот код находится в Box SDK, единственное, что мне удалось попробовать, - это использование BoxConnectionManager (который обеспечивает поточно-ориентированное).

Но когда я использовал BoxConnectionManager Я столкнулся со второй упомянутой ошибкой: имя хоста в сертификате не совпадало. Эта ошибка связана с различными версиями HTTP-клиента Apache на пути к классам. Мой сервер (eXo Platform 4.0) предоставляет HTTP-клиент Apache 4.1.2, но для Box SDK требуется 4.2.5. После установки моего приложения серверные библиотеки имеют оба этих JAR-файла, и 4.1.2 отображается как загруженный для Box. Если удалить JAR из 4.1.2, то все работает как положено, и те, кто может сделать это, не столкнутся с ошибкой SSL-сертификата, как я. Но я не могу сделать это, так как другие части eXo сертифицированы с помощью HTTP-клиента 4.1.2, и я не хочу рисковать ими.

Мое решение - использование пользовательских BoxRESTClient в BoxClient, Благодаря команде Box можно создать экземпляр своего клиента с деталями низкого уровня, такими как клиент REST. BoxConnectionManager скрывает это, предоставляя собственный BoxRESTClient, но также возможно создать этот клиент REST из внешнего кода, без использования менеджера. Мой пользовательский REST-клиент адаптирован к HTTP-клиенту 4.1.2 и имеет "разрешающий всем" верификатор имени хоста.

Пока что мое решение приемлемо и работает хорошо. Ниже фрагментов кода, которые я использовал:

Как создать Box-клиент:

BoxResourceHub hub = new BoxResourceHub();
BoxJSONParser parser = new BoxJSONParser(hub);
this.client = new BoxClient(key,
                            clientSecret,
                            hub,
                            parser,
                            new RESTClient(),
                            new BoxConfigBuilder().build());

И обычай RESTClient:

class RESTClient extends BoxRESTClient {

  final HttpClient httpClient;

  @SuppressWarnings("deprecation")
  RESTClient() {
    super();
    SchemeRegistry schemeReg = new SchemeRegistry();
    schemeReg.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
    SSLSocketFactory socketFactory;
    try {
      socketFactory = new SSLSocketFactory(SSLSocketFactory.TLS,
                                         null,
                                         null,
                                         null,
                                         null,
                                         null,
                                         SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
    } catch (Exception ex) {
      throw new IllegalStateException("Failure initializing default SSL context for Box REST client", ex);
    }
    schemeReg.register(new Scheme("https", socketFactory, 443));
    ClientConnectionManager connectionManager = new ThreadSafeClientConnManager(new BasicHttpParams(), schemeReg);
    this.httpClient = new DefaultHttpClient(connectionManager);
  }

  @Override
  public HttpClient getRawHttpClient() {
    return httpClient;
  }
}

Таким образом Box SDK работает хорошо и может загружать несколько файлов в пользовательский ящик.

Из трассировки стека это выглядит так, как будто ssl-сертификат не установлен. Можете ли вы попробовать запустить оба кода (с и без ConnectionManager) на одном компьютере? Один потерпит неудачу, а другой преуспеет?

Другие вопросы по тегам