Package com.google.api.client.googleapis.batch

Source Code of com.google.api.client.googleapis.batch.BatchUnparsedResponse$FakeResponseHttpTransport

/*
* Copyright (c) 2012 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/

package com.google.api.client.googleapis.batch;

import com.google.api.client.googleapis.GoogleHeaders;
import com.google.api.client.googleapis.batch.BatchRequest.RequestInfo;
import com.google.api.client.http.BackOffPolicy;
import com.google.api.client.http.GenericUrl;
import com.google.api.client.http.HttpContent;
import com.google.api.client.http.HttpRequest;
import com.google.api.client.http.HttpResponse;
import com.google.api.client.http.HttpStatusCodes;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.http.HttpUnsuccessfulResponseHandler;
import com.google.api.client.http.LowLevelHttpRequest;
import com.google.api.client.http.LowLevelHttpResponse;
import com.google.api.client.util.ObjectParser;
import com.google.api.client.util.StringUtils;
import com.google.common.base.Preconditions;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;


/**
* The unparsed batch response.
*
* @author rmistry@google.com (Ravi Mistry)
*/
final class BatchUnparsedResponse {

  /** The boundary used in the batch response to separate individual responses. */
  private final String boundary;

  /** List of request infos. */
  private final List<RequestInfo<?, ?>> requestInfos;

  /** Buffers characters from the input stream. */
  private final BufferedReader bufferedReader;

  /** Determines whether there are any responses to be parsed. */
  boolean hasNext = true;

  /** List of unsuccessful HTTP requests that can be retried. */
  List<RequestInfo<?, ?>> unsuccessfulRequestInfos = new ArrayList<RequestInfo<?, ?>>();

  /** Indicates if back off is required before the next retry. */
  boolean backOffRequired;

  /** The content Id the response is currently at. */
  private int contentId = 0;

  /** Whether unsuccessful HTTP requests can be retried. */
  private final boolean retryAllowed;

  /**
   * Construct the {@link BatchUnparsedResponse}.
   *
   * @param inputStream Input stream that contains the batch response
   * @param boundary The boundary of the batch response
   * @param requestInfos List of request infos
   * @param retryAllowed Whether unsuccessful HTTP requests can be retried
   */
  BatchUnparsedResponse(InputStream inputStream, String boundary,
      List<RequestInfo<?, ?>> requestInfos, boolean retryAllowed)
      throws IOException {
    this.boundary = boundary;
    this.requestInfos = requestInfos;
    this.retryAllowed = retryAllowed;
    this.bufferedReader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
    // First line in the stream will be the boundary.
    checkForFinalBoundary(bufferedReader.readLine());
  }

  /**
   * Parses the next response in the queue if a data class and a {@link BatchCallback} is specified.
   *
   * <p>
   * This method closes the input stream if there are no more individual responses left.
   * </p>
   */
  void parseNextResponse() throws IOException {
    contentId++;

    // Extract the outer headers.
    String line;
    while ((line = bufferedReader.readLine()) != null && !line.equals("")) {
      // Do nothing.
    }

    // Extract the status code.
    String statusLine = bufferedReader.readLine();
    Preconditions.checkState(statusLine != null);
    String[] statusParts = statusLine.split(" ");
    int statusCode = Integer.parseInt(statusParts[1]);

    // Extract and store the inner headers.
    // TODO(rmistry): Handle inner headers that span multiple lines. More details here:
    // http://tools.ietf.org/html/rfc2616#section-2.2
    List<String> headerNames = new ArrayList<String>();
    List<String> headerValues = new ArrayList<String>();
    while ((line = bufferedReader.readLine()) != null && !line.equals("")) {
      String[] headerParts = line.split(": ", 2);
      headerNames.add(headerParts[0]);
      headerValues.add(headerParts[1]);
    }

    // Extract the response part content.
    // TODO(rmistry): Investigate a way to use the stream directly. This is to reduce the chance of
    // an OutOfMemoryError and will make parsing more efficient.
    StringBuilder partContent = new StringBuilder();
    while ((line = bufferedReader.readLine()) != null && !line.startsWith(boundary)) {
      partContent.append(line);
    }

    HttpResponse response =
        getFakeResponse(statusCode, partContent.toString(), headerNames, headerValues);

    parseAndCallback(requestInfos.get(contentId - 1), statusCode, contentId, response);

    checkForFinalBoundary(line);
  }

  /**
   * Parse an object into a new instance of the data class using
   * {@link HttpResponse#parseAs(java.lang.reflect.Type)}.
   */
  private <T, E> void parseAndCallback(
      RequestInfo<T, E> requestInfo, int statusCode, int contentID, HttpResponse response)
      throws IOException {
    BatchCallback<T, E> callback = requestInfo.callback;

    GoogleHeaders responseHeaders = new GoogleHeaders(response.getHeaders());
    HttpUnsuccessfulResponseHandler unsuccessfulResponseHandler =
        requestInfo.request.getUnsuccessfulResponseHandler();
    BackOffPolicy backOffPolicy = requestInfo.request.getBackOffPolicy();

    // Reset backOff flag.
    backOffRequired = false;

    if (HttpStatusCodes.isSuccess(statusCode)) {
      if (callback == null) {
        // No point in parsing if there is no callback.
        return;
      }
      T parsed = getParsedDataClass(
          requestInfo.dataClass, response, requestInfo, responseHeaders.getContentType());
      callback.onSuccess(parsed, responseHeaders);
    } else {
      HttpContent content = requestInfo.request.getContent();
      boolean retrySupported = retryAllowed && (content == null || content.retrySupported());
      boolean errorHandled = false;
      boolean redirectRequest = false;
      if (unsuccessfulResponseHandler != null) {
        errorHandled = unsuccessfulResponseHandler.handleResponse(
            requestInfo.request, response, retrySupported);
      }
      if (!errorHandled) {
        if (requestInfo.request.handleRedirect(response.getStatusCode(), response.getHeaders())) {
          redirectRequest = true;
        } else if (retrySupported && backOffPolicy != null
            && backOffPolicy.isBackOffRequired(response.getStatusCode())) {
          backOffRequired = true;
        }
      }
      if (retrySupported && (errorHandled || backOffRequired || redirectRequest)) {
        unsuccessfulRequestInfos.add(requestInfo);
      } else {
        if (callback == null) {
          // No point in parsing if there is no callback.
          return;
        }
        E parsed = getParsedDataClass(
            requestInfo.errorClass, response, requestInfo, responseHeaders.getContentType());
        callback.onFailure(parsed, responseHeaders);
      }
    }
  }

  @SuppressWarnings("deprecation")
  private <A, T, E> A getParsedDataClass(
      Class<A> dataClass, HttpResponse response, RequestInfo<T, E> requestInfo, String contentType)
      throws IOException {
    // TODO(mlinder): Remove the HttpResponse reference and directly parse the InputStream
    com.google.api.client.http.HttpParser oldParser = requestInfo.request.getParser(contentType);
    ObjectParser parser = requestInfo.request.getParser();
    A parsed = null;
    if (dataClass != Void.class) {
      parsed = parser != null ? parser.parseAndClose(
          response.getContent(), response.getContentCharset(), dataClass) : oldParser.parse(
          response, dataClass);
    }
    return parsed;
  }

  /** Create a fake HTTP response object populated with the partContent and the statusCode. */
  @Deprecated
  private HttpResponse getFakeResponse(final int statusCode, final String partContent,
      List<String> headerNames, List<String> headerValues)
      throws IOException {
    HttpRequest request = new FakeResponseHttpTransport(
        statusCode, partContent, headerNames, headerValues).createRequestFactory()
        .buildPostRequest(new GenericUrl("http://google.com/"), null);
    request.setLoggingEnabled(false);
    request.setThrowExceptionOnExecuteError(false);
    return request.execute();
  }

  /**
   * If the boundary line consists of the boundary and "--" then there are no more individual
   * responses left to be parsed and the input stream is closed.
   */
  private void checkForFinalBoundary(String boundaryLine) throws IOException {
    if (boundaryLine.equals(boundary + "--")) {
      hasNext = false;
      bufferedReader.close();
    }
  }

  @Deprecated
  private static class FakeResponseHttpTransport extends HttpTransport {

    private int statusCode;
    private String partContent;
    private List<String> headerNames;
    private List<String> headerValues;

    FakeResponseHttpTransport(
        int statusCode, String partContent, List<String> headerNames, List<String> headerValues) {
      super();
      this.statusCode = statusCode;
      this.partContent = partContent;
      this.headerNames = headerNames;
      this.headerValues = headerValues;
    }

    @Override
    protected LowLevelHttpRequest buildDeleteRequest(String url) {
      return null;
    }

    @Override
    protected LowLevelHttpRequest buildGetRequest(String url) {
      return null;
    }

    @Override
    protected LowLevelHttpRequest buildPostRequest(String url) {
      return new FakeLowLevelHttpRequest(partContent, statusCode, headerNames, headerValues);
    }

    @Override
    protected LowLevelHttpRequest buildPutRequest(String url) {
      return null;
    }
  }

  @Deprecated
  private static class FakeLowLevelHttpRequest extends LowLevelHttpRequest {

    // TODO(rmistry): Read in partContent as bytes instead of String for efficiency.
    private String partContent;
    private int statusCode;
    private List<String> headerNames;
    private List<String> headerValues;

    FakeLowLevelHttpRequest(
        String partContent, int statusCode, List<String> headerNames, List<String> headerValues) {
      this.partContent = partContent;
      this.statusCode = statusCode;
      this.headerNames = headerNames;
      this.headerValues = headerValues;
    }

    @Override
    public void addHeader(String name, String value) {
    }

    @Override
    public void setContent(HttpContent content) {
    }

    @Override
    public LowLevelHttpResponse execute() {
      FakeLowLevelHttpResponse response = new FakeLowLevelHttpResponse(new ByteArrayInputStream(
          StringUtils.getBytesUtf8(partContent)), statusCode, headerNames, headerValues);
      return response;
    }
  }

  @Deprecated
  private static class FakeLowLevelHttpResponse extends LowLevelHttpResponse {

    private InputStream partContent;
    private int statusCode;
    private List<String> headerNames = new ArrayList<String>();
    private List<String> headerValues = new ArrayList<String>();

    FakeLowLevelHttpResponse(InputStream partContent, int statusCode, List<String> headerNames,
        List<String> headerValues) {
      this.partContent = partContent;
      this.statusCode = statusCode;
      this.headerNames = headerNames;
      this.headerValues = headerValues;
    }

    @Override
    public InputStream getContent() {
      return partContent;
    }

    @Override
    public int getStatusCode() {
      return statusCode;
    }

    @Override
    public String getContentEncoding() {
      return null;
    }

    @Override
    public long getContentLength() {
      return 0;
    }

    @Override
    public String getContentType() {
      return null;
    }

    @Override
    public String getStatusLine() {
      return null;
    }

    @Override
    public String getReasonPhrase() {
      return null;
    }

    @Override
    public int getHeaderCount() {
      return headerNames.size();
    }

    @Override
    public String getHeaderName(int index) {
      return headerNames.get(index);
    }

    @Override
    public String getHeaderValue(int index) {
      return headerValues.get(index);
    }
  }
}
TOP

Related Classes of com.google.api.client.googleapis.batch.BatchUnparsedResponse$FakeResponseHttpTransport

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.