@Test
  public void invalidAbsoluteUri() throws URISyntaxException {
    final ODataBatchRequest request =
        getClient().getBatchRequestFactory().getBatchRequest(SERVICE_URI);

    final BatchManager payload = request.payloadManager();
    final URI uri = new URI(SERVICE_URI + "../ESAllPrim(32767)");
    final ODataEntityRequest<ClientEntity> queryReq =
        getClient().getRetrieveRequestFactory().getEntityRequest(uri);
    queryReq.setFormat(ContentType.JSON);
    payload.addRequest(queryReq);

    // Fetch result
    final ODataBatchResponse response = payload.getResponse();
    assertEquals(HttpStatusCode.ACCEPTED.getStatusCode(), response.getStatusCode());

    final Iterator<ODataBatchResponseItem> bodyIterator = response.getBody();
    assertTrue(bodyIterator.hasNext());

    ODataBatchResponseItem item = bodyIterator.next();
    assertFalse(item.isChangeset());

    final ODataResponse oDataResponse = item.next();
    assertEquals(HttpStatusCode.BAD_REQUEST.getStatusCode(), oDataResponse.getStatusCode());
  }
    @Override
    public R getODataResponse() {
      HttpResponse res = null;
      for (int i = 0; response == null && i < MAX_RETRY; i++) {
        res = checkMonitor(location);

        if (res.getStatusLine().getStatusCode() == HttpStatusCode.ACCEPTED.getStatusCode()) {

          final Header[] headers = res.getHeaders(HttpHeader.RETRY_AFTER);
          if (ArrayUtils.isNotEmpty(headers)) {
            this.retryAfter = Integer.parseInt(headers[0].getValue());
          }

          try {
            // wait for retry-after
            Thread.sleep(retryAfter * 1000);
          } catch (InterruptedException ignore) {
            // ignore
          }

        } else {
          location = null;
          return instantiateResponse(res);
        }
      }

      if (response == null) {
        throw new ODataClientErrorException(res == null ? null : res.getStatusLine());
      }

      return response;
    }
  @Test
  public void getBatchRequest() throws URISyntaxException {
    final ODataBatchRequest request =
        getClient().getBatchRequestFactory().getBatchRequest(SERVICE_URI);

    final BatchManager payload = request.payloadManager();

    // create new request
    appendGetRequest(payload, "ESAllPrim", 32767, false);

    // Fetch result
    final ODataBatchResponse response = payload.getResponse();

    assertEquals(HttpStatusCode.ACCEPTED.getStatusCode(), response.getStatusCode());
    assertEquals("Accepted", response.getStatusMessage());

    final Iterator<ODataBatchResponseItem> iter = response.getBody();
    assertTrue(iter.hasNext());

    ODataBatchResponseItem item = iter.next();
    assertFalse(item.isChangeset());

    ODataResponse oDataResponse = item.next();
    assertNotNull(oDataResponse);
    assertEquals(HttpStatusCode.OK.getStatusCode(), oDataResponse.getStatusCode());
    assertEquals(1, oDataResponse.getHeader("OData-Version").size());
    assertEquals("4.0", oDataResponse.getHeader("OData-Version").toArray()[0]);
    assertEquals(1, oDataResponse.getHeader("Content-Length").size());
    assertEquals("605", oDataResponse.getHeader("Content-Length").toArray()[0]);
    assertEquals(ContentType.JSON.toContentTypeString(), oDataResponse.getContentType());
  }
  @Test
  public void errorWithContinueOnErrorPreferHeader() throws Exception {
    ODataClient client = getClient();
    client.getConfiguration().setContinueOnError(true);
    final ODataBatchRequest request = client.getBatchRequestFactory().getBatchRequest(SERVICE_URI);

    final BatchManager payload = request.payloadManager();

    appendGetRequest(payload, "ESAllPrim", 32767, false); // Without error
    appendGetRequest(payload, "ESAllPrim", 42, false); // Error ( Key does not exist )
    appendGetRequest(payload, "ESAllPrim", 0, false); // Without error

    // Fetch result
    final ODataBatchResponse response = payload.getResponse();
    assertEquals(HttpStatusCode.ACCEPTED.getStatusCode(), response.getStatusCode());
    assertEquals(
        PreferenceName.CONTINUE_ON_ERROR.getName(),
        response.getHeader(HttpHeader.PREFERENCE_APPLIED).iterator().next());

    final Iterator<ODataBatchResponseItem> bodyIterator = response.getBody();

    // Check first get request
    assertTrue(bodyIterator.hasNext());
    ODataBatchResponseItem item = bodyIterator.next();
    assertFalse(item.isChangeset());

    ODataResponse oDataResponse = item.next();
    assertNotNull(oDataResponse);
    assertEquals(HttpStatusCode.OK.getStatusCode(), oDataResponse.getStatusCode());
    assertEquals(1, oDataResponse.getHeader(HttpHeader.ODATA_VERSION).size());
    assertEquals("4.0", oDataResponse.getHeader(HttpHeader.ODATA_VERSION).toArray()[0]);
    assertEquals(1, oDataResponse.getHeader(HttpHeader.CONTENT_LENGTH).size());
    assertEquals("605", oDataResponse.getHeader(HttpHeader.CONTENT_LENGTH).toArray()[0]);
    assertEquals(ContentType.JSON.toContentTypeString(), oDataResponse.getContentType());

    // Check second get request
    assertTrue(bodyIterator.hasNext());
    item = bodyIterator.next();
    assertFalse(item.isChangeset());

    oDataResponse = item.next();
    assertNotNull(oDataResponse);
    assertEquals(HttpStatusCode.NOT_FOUND.getStatusCode(), oDataResponse.getStatusCode());

    // Check if third request is available
    assertTrue(bodyIterator.hasNext());
    item = bodyIterator.next();
    assertFalse(item.isChangeset());

    oDataResponse = item.next();
    assertNotNull(oDataResponse);
    assertEquals(HttpStatusCode.OK.getStatusCode(), oDataResponse.getStatusCode());
    assertEquals(1, oDataResponse.getHeader(HttpHeader.ODATA_VERSION).size());
    assertEquals("4.0", oDataResponse.getHeader(HttpHeader.ODATA_VERSION).iterator().next());
    assertEquals(1, oDataResponse.getHeader(HttpHeader.CONTENT_LENGTH).size());
    assertEquals("517", oDataResponse.getHeader(HttpHeader.CONTENT_LENGTH).iterator().next());
    assertEquals(ContentType.JSON.toContentTypeString(), oDataResponse.getContentType());
  }
  @Test
  public void badRequestInChangeSet() {
    /*
     * A bad request (status code >= 400) without "continue on error prefer header" in a changeset
     * should return a single response with Content-Type: application/http
     *
     * See:
     * OData Version 4.0 Part 1: Protocol Plus Errata 01
     * 11.7.4 Responding to a Batch Request
     *
     * When a request within a change set fails, the change set response is not represented using
     * the multipart/mixed media type. Instead, a single response, using the application/http media type
     * and a Content-Transfer-Encoding header with a value of binary, is returned that applies to all requests
     * in the change set and MUST be formatted according to the Error Handling defined
     * for the particular response format.
     */

    // Try to create entity, with invalid type
    ClientObjectFactory factory = getFactory();
    final ClientEntity entity = factory.newEntity(ES_NOT_AVAILABLE);
    entity
        .getProperties()
        .add(
            factory.newPrimitiveProperty(
                PROPERTY_STRING, factory.newPrimitiveValueBuilder().buildString("1")));
    final ODataBatchRequest batchRequest =
        getClient().getBatchRequestFactory().getBatchRequest(SERVICE_URI);
    final BatchManager payloadManager = batchRequest.payloadManager();
    final ODataChangeset changeset = payloadManager.addChangeset();
    final URI targetURI =
        getClient()
            .newURIBuilder(SERVICE_URI)
            .appendEntitySetSegment(ES_NOT_AVAILABLE_NAME)
            .build();
    final ODataEntityCreateRequest<ClientEntity> createRequest =
        getClient().getCUDRequestFactory().getEntityCreateRequest(targetURI, entity);
    changeset.addRequest(createRequest);

    final ODataBatchResponse response = payloadManager.getResponse();
    assertEquals(HttpStatusCode.ACCEPTED.getStatusCode(), response.getStatusCode());

    // Check response items
    final Iterator<ODataBatchResponseItem> responseBodyIter = response.getBody();
    assertTrue(responseBodyIter.hasNext());

    final ODataBatchResponseItem changeSetResponse = responseBodyIter.next();
    assertTrue(changeSetResponse.isChangeset());
    assertTrue(changeSetResponse.hasNext());

    final ODataResponse updateResponse = changeSetResponse.next();
    assertTrue(changeSetResponse.isBreaking());

    assertEquals(HttpStatusCode.NOT_FOUND.getStatusCode(), updateResponse.getStatusCode());
    assertContentType(updateResponse.getContentType());
  }
  @Test
  public void emptyBatchRequest() {
    // create your request
    final ODataBatchRequest request =
        getClient().getBatchRequestFactory().getBatchRequest(SERVICE_URI);

    final BatchManager payload = request.payloadManager();
    final ODataBatchResponse response = payload.getResponse();

    assertEquals(HttpStatusCode.ACCEPTED.getStatusCode(), response.getStatusCode());
    assertEquals("Accepted", response.getStatusMessage());

    final Iterator<ODataBatchResponseItem> iter = response.getBody();
    assertFalse(iter.hasNext());
  }
  @Test
  public void errorWithoutContinueOnErrorPreferHeader() throws URISyntaxException {
    final ODataBatchRequest request =
        getClient().getBatchRequestFactory().getBatchRequest(SERVICE_URI);

    final BatchManager payload = request.payloadManager();

    appendGetRequest(payload, "ESAllPrim", 32767, false); // Without error
    appendGetRequest(payload, "ESAllPrim", 42, false); // Error ( Key does not exist )
    appendGetRequest(payload, "ESAllPrim", 0, false); // Without error

    // Fetch result
    final ODataBatchResponse response = payload.getResponse();
    assertEquals(HttpStatusCode.ACCEPTED.getStatusCode(), response.getStatusCode());

    final Iterator<ODataBatchResponseItem> iter = response.getBody();

    // Check first get request
    assertTrue(iter.hasNext());
    ODataBatchResponseItem item = iter.next();
    assertFalse(item.isChangeset());

    ODataResponse oDataResponse = item.next();
    assertNotNull(oDataResponse);
    assertEquals(HttpStatusCode.OK.getStatusCode(), oDataResponse.getStatusCode());
    assertEquals(1, oDataResponse.getHeader("OData-Version").size());
    assertEquals("4.0", oDataResponse.getHeader("OData-Version").toArray()[0]);
    assertEquals(1, oDataResponse.getHeader("Content-Length").size());
    assertEquals("605", oDataResponse.getHeader("Content-Length").toArray()[0]);
    assertEquals(ContentType.JSON.toContentTypeString(), oDataResponse.getContentType());

    // Check second get request
    assertTrue(iter.hasNext());
    item = iter.next();
    assertFalse(item.isChangeset());

    oDataResponse = item.next();
    assertNotNull(oDataResponse);
    assertEquals(HttpStatusCode.NOT_FOUND.getStatusCode(), oDataResponse.getStatusCode());

    // Check if third request is available
    assertFalse(iter.hasNext());
  }
  @Test
  public void changesetBatchRequest() throws URISyntaxException {
    final ODataBatchRequest request =
        getClient().getBatchRequestFactory().getBatchRequest(SERVICE_URI);
    final BatchManager payload = request.payloadManager();
    // -----------------------------
    // - Append get request
    // -----------------------------
    appendGetRequest(payload, "ESAllPrim", 32767, false); // Without error

    // -----------------------------
    // - Append change set
    // -----------------------------
    final ODataChangeset changeset = payload.addChangeset();

    // ------------------------
    // POST request (Insert)
    URIBuilder targetURI =
        getClient().newURIBuilder(SERVICE_URI).appendEntitySetSegment("ESAllPrim");
    URI editLink = targetURI.build();

    ClientObjectFactory factory = getFactory();
    ClientEntity postEntity =
        factory.newEntity(new FullQualifiedName(SERVICE_NAMESPACE, "ESAllPrim"));
    postEntity.addLink(
        factory.newEntityNavigationLink(
            "NavPropertyETTwoPrimOne",
            getClient()
                .newURIBuilder(SERVICE_URI)
                .appendEntitySetSegment("ESTwoPrim")
                .appendKeySegment(32766)
                .build()));

    postEntity
        .getProperties()
        .add(
            factory.newPrimitiveProperty(
                "PropertyDouble", factory.newPrimitiveValueBuilder().buildDouble(3.1415)));

    final ODataEntityCreateRequest<ClientEntity> createRequest =
        getClient().getCUDRequestFactory().getEntityCreateRequest(editLink, postEntity);
    createRequest.setFormat(ContentType.JSON);

    changeset.addRequest(createRequest);

    // ------------------------
    // Patch request (Update)
    targetURI =
        getClient()
            .newURIBuilder(SERVICE_URI)
            .appendEntitySetSegment("ESAllPrim")
            .appendKeySegment(0);
    editLink = targetURI.build();

    ClientEntity patchEntity =
        factory.newEntity(new FullQualifiedName(SERVICE_NAMESPACE, "ESAllPrim"));
    patchEntity.setEditLink(editLink);

    patchEntity
        .getProperties()
        .add(
            factory.newPrimitiveProperty(
                "PropertyDouble", factory.newPrimitiveValueBuilder().buildDouble(3.1415)));

    ODataEntityUpdateRequest<ClientEntity> changeReq =
        getClient().getCUDRequestFactory().getEntityUpdateRequest(UpdateType.PATCH, patchEntity);
    changeReq.setFormat(ContentType.JSON);
    changeset.addRequest(changeReq);

    // ------------------------
    // Patch request (Upsert)
    targetURI =
        getClient()
            .newURIBuilder(SERVICE_URI)
            .appendEntitySetSegment("ESAllPrim")
            .appendKeySegment(15);
    editLink = targetURI.build();

    patchEntity = factory.newEntity(new FullQualifiedName(SERVICE_NAMESPACE, "ESAllPrim"));
    patchEntity.setEditLink(editLink);

    patchEntity
        .getProperties()
        .add(
            factory.newPrimitiveProperty(
                "PropertyDouble", factory.newPrimitiveValueBuilder().buildDouble(3.1415)));

    patchEntity.addLink(
        factory.newEntityNavigationLink(
            "NavPropertyETTwoPrimOne",
            getClient()
                .newURIBuilder(SERVICE_URI)
                .appendEntitySetSegment("ESTwoPrim")
                .appendKeySegment(32766)
                .build()));

    changeReq =
        getClient().getCUDRequestFactory().getEntityUpdateRequest(UpdateType.PATCH, patchEntity);
    changeReq.setFormat(ContentType.JSON);
    changeset.addRequest(changeReq);

    // -----------------------------
    // - Append get request
    // -----------------------------
    appendGetRequest(payload, "ESAllPrim", 0, false); // Without error

    // -----------------------------
    // - Fetch result
    // -----------------------------
    final ODataBatchResponse response = payload.getResponse();
    assertEquals(HttpStatusCode.ACCEPTED.getStatusCode(), response.getStatusCode());
    final Iterator<ODataBatchResponseItem> bodyIterator = response.getBody();

    // Check first get request
    assertTrue(bodyIterator.hasNext());
    ODataBatchResponseItem item = bodyIterator.next();
    assertFalse(item.isChangeset());
    assertTrue(item.hasNext());
    final ODataResponse response0 = item.next();
    assertTrue(response0 instanceof ODataRetrieveResponse);
    @SuppressWarnings("unchecked")
    ODataRetrieveResponse<ClientEntity> retrieveResponse =
        (ODataRetrieveResponse<ClientEntity>) response0;
    assertEquals(
        34,
        retrieveResponse.getBody().getProperty("PropertyDecimal").getPrimitiveValue().toValue());

    // Check change set
    assertTrue(bodyIterator.hasNext());
    item = bodyIterator.next();
    assertTrue(item.isChangeset());

    // Insert
    assertTrue(item.hasNext());
    final ODataResponse response1 = item.next();
    assertEquals(HttpStatusCode.CREATED.getStatusCode(), response1.getStatusCode());
    assertTrue(response1 instanceof ODataEntityCreateResponse);
    assertEquals(
        3.1415,
        ((ODataEntityCreateResponse<?>) response1)
            .getBody()
            .getProperty("PropertyDouble")
            .getPrimitiveValue()
            .toValue());
    // Update
    assertTrue(item.hasNext());
    final ODataResponse response2 = item.next();
    assertEquals(HttpStatusCode.OK.getStatusCode(), response2.getStatusCode());
    assertTrue(response2 instanceof ODataEntityUpdateResponse);

    // Upsert
    assertTrue(item.hasNext());
    final ODataResponse response3 = item.next();
    assertEquals(HttpStatusCode.CREATED.getStatusCode(), response3.getStatusCode());
    assertTrue(response3 instanceof ODataEntityUpdateResponse);
    assertEquals(
        3.1415,
        ((ODataEntityUpdateResponse<?>) response3)
            .getBody()
            .getProperty("PropertyDouble")
            .getPrimitiveValue()
            .toValue());

    // Check second get request
    assertTrue(bodyIterator.hasNext());
    item = bodyIterator.next();
    assertFalse(item.isChangeset());
    assertTrue(item.hasNext());
    final ODataResponse response4 = item.next();
    assertTrue(response4 instanceof ODataRetrieveResponse);
    @SuppressWarnings("unchecked")
    final ODataRetrieveResponse<ClientEntity> retrieveResponse2 =
        (ODataRetrieveResponse<ClientEntity>) response4;
    assertEquals(
        3.1415,
        retrieveResponse2.getBody().getProperty("PropertyDouble").getPrimitiveValue().toValue());
  }
  @Test
  public void changesetWithReferences() throws EdmPrimitiveTypeException, URISyntaxException {
    // create your request
    final ODataBatchRequest request =
        getClient().getBatchRequestFactory().getBatchRequest(SERVICE_URI);
    final BatchManager streamManager = request.payloadManager();

    final ODataChangeset changeset = streamManager.addChangeset();
    final ClientEntity entityESAllPrim =
        getFactory().newEntity(new FullQualifiedName(SERVICE_NAMESPACE, "ESAllPrim"));

    entityESAllPrim
        .getProperties()
        .add(
            getFactory()
                .newPrimitiveProperty(
                    "PropertyDouble", getFactory().newPrimitiveValueBuilder().buildDouble(3.1415)));

    entityESAllPrim.addLink(
        getFactory()
            .newEntityNavigationLink(
                "NavPropertyETTwoPrimOne",
                getClient()
                    .newURIBuilder(SERVICE_URI)
                    .appendEntitySetSegment("ESTwoPrim")
                    .appendKeySegment(-365)
                    .build()));

    final URIBuilder uriBuilder =
        getClient().newURIBuilder(SERVICE_URI).appendEntitySetSegment("ESAllPrim");

    // add create request
    final ODataEntityCreateRequest<ClientEntity> createReq =
        getClient()
            .getCUDRequestFactory()
            .getEntityCreateRequest(uriBuilder.build(), entityESAllPrim);
    createReq.setFormat(ContentType.JSON);
    changeset.addRequest(createReq);

    // retrieve request reference
    int createRequestRef = changeset.getLastContentId();

    // add update request
    final ClientEntity entityUpdate = getFactory().newEntity(entityESAllPrim.getTypeName());
    entityUpdate.addLink(
        getFactory()
            .newEntitySetNavigationLink(
                "NavPropertyETTwoPrimMany",
                getClient()
                    .newURIBuilder(SERVICE_URI)
                    .appendEntitySetSegment("ESTwoPrim")
                    .appendKeySegment(32767)
                    .build()));

    final ODataEntityUpdateRequest<ClientEntity> updateReq =
        getClient()
            .getCUDRequestFactory()
            .getEntityUpdateRequest(
                URI.create("$" + createRequestRef), UpdateType.PATCH, entityUpdate);
    updateReq.setFormat(ContentType.JSON);

    changeset.addRequest(updateReq);

    final ODataBatchResponse response = streamManager.getResponse();
    assertEquals(HttpStatusCode.ACCEPTED.getStatusCode(), response.getStatusCode());
    final String cookie = response.getHeader(HttpHeader.SET_COOKIE).iterator().next();

    // verify response payload ...
    final Iterator<ODataBatchResponseItem> bodyIterator = response.getBody();
    final ODataBatchResponseItem item = bodyIterator.next();

    assertTrue(item instanceof ODataChangesetResponseItem);
    final ODataChangesetResponseItem chgitem = (ODataChangesetResponseItem) item;
    assertTrue(chgitem.hasNext());
    ODataResponse res = chgitem.next();
    assertEquals(HttpStatusCode.CREATED.getStatusCode(), res.getStatusCode());
    assertTrue(res instanceof ODataEntityCreateResponse);
    final ODataEntityCreateResponse<?> createResponse = ((ODataEntityCreateResponse<?>) res);

    res = chgitem.next();
    assertEquals(HttpStatusCode.OK.getStatusCode(), res.getStatusCode());
    assertTrue(res instanceof ODataEntityUpdateResponse);

    final ODataEntitySetRequest<ClientEntitySet> req =
        getClient()
            .getRetrieveRequestFactory()
            .getEntitySetRequest(
                new URI(
                    createResponse.getHeader(HttpHeader.LOCATION).iterator().next()
                        + "/NavPropertyETTwoPrimMany"));
    req.setFormat(ContentType.JSON);
    req.addCustomHeader(HttpHeader.COOKIE, cookie);
    final ODataRetrieveResponse<ClientEntitySet> getResponse = req.execute();

    assertEquals(
        32767,
        getResponse
            .getBody()
            .getEntities()
            .get(0)
            .getProperty("PropertyInt16")
            .getPrimitiveValue()
            .toValue());
  }