All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.facebook.ads.sdk.APIRequest Maven / Gradle / Ivy

There is a newer version: 2.11.1
Show newest version
/**
 * Copyright (c) 2015-present, Facebook, Inc. All rights reserved.
 *
 * You are hereby granted a non-exclusive, worldwide, royalty-free license to
 * use, copy, modify, and distribute this software in source code or binary
 * form for use in connection with the web services and APIs provided by
 * Facebook.
 *
 * As with any software that integrates with the Facebook platform, your use
 * of this software is subject to the Facebook Developer Principles and
 * Policies [http://developers.facebook.com/policy/]. This copyright notice
 * shall be included in all copies or substantial portions of the software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */
package com.facebook.ads.sdk;

import java.nio.file.Files;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.net.URL;
import java.net.URLEncoder;
import javax.net.ssl.HttpsURLConnection;
import java.io.BufferedReader;
import java.lang.reflect.Modifier;
import java.lang.StringBuilder;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.io.IOException;
import java.util.Random;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

public class APIRequest {

  public static final String USER_AGENT = APIConfig.USER_AGENT;

  private static IRequestExecutor executor = new DefaultRequestExecutor();

  protected APIContext context;
  protected boolean useVideoEndpoint = false;
  protected String nodeId;
  protected String endpoint;
  protected String method;
  protected List paramNames;
  protected ResponseParser parser;
  protected Map params = new HashMap();
  protected List returnFields;
  private APIResponse lastResponse = null;

  public static void changeRequestExecutor(IRequestExecutor newExecutor) {
    executor = newExecutor;
  }

  public static IRequestExecutor getExecutor() {
    return executor;
  }

  public APIRequest(APIContext context, String nodeId, String endpoint, String method) {
    this(context, nodeId, endpoint, method, null, null);
  }

  public APIRequest(APIContext context, String nodeId, String endpoint, String method, ResponseParser parser) {
    this(context, nodeId, endpoint, method, null, parser);
  }

  public APIRequest(APIContext context, String nodeId, String endpoint, String method, List paramNames) {
    this(context, nodeId, endpoint, method, paramNames, null);
  }

  public APIRequest(APIContext context, String nodeId, String endpoint, String method, List paramNames, ResponseParser parser) {
    this.context = context;
    this.nodeId = nodeId;
    this.endpoint = endpoint;
    this.method = method;
    this.paramNames = paramNames;
    this.parser = parser;
  }

  public APIResponse getLastResponse() {
    return lastResponse;
  };

  public APIResponse parseResponse(String response) throws APIException {
    if (parser != null) {
      return parser.parseResponse(response, context, this);
    } else {
      return APINode.parseResponse(response, context, new APIRequest(context, nodeId, endpoint, method, paramNames));
    }
  };

  public APIResponse execute() throws APIException {
    return execute(new HashMap());
  };

  public APIResponse execute(Map extraParams) throws APIException {
    lastResponse = parseResponse(executeInternal(extraParams));
    return lastResponse;
  };

  public APIRequest setParam(String param, Object value) {
    setParamInternal(param, value);
    return this;
  }

  public APIRequest setParams(Map params) {
    setParamsInternal(params);
    return this;
  }

  public APIRequest requestFields(List fields) {
    return this.requestFields(fields, true);
  }

  public APIRequest requestFields(List fields, boolean value) {
    for (String field : fields) {
      this.requestField(field, value);
    }
    return this;
  }

  public APIRequest requestField(String field) {
    this.requestField(field, true);
    return this;
  }

  public APIRequest requestField(String field, boolean value) {
    this.requestFieldInternal(field, value);
    return this;
  }

  public APIRequest setUseVideoEndpoint(boolean useVideoEndpoint) {
    this.useVideoEndpoint = useVideoEndpoint;
    return this;
  }

  protected String executeInternal() throws APIException {
    return executeInternal(null);
  }

  protected String executeInternal(Map extraParams) throws APIException {
    // extraParams are one-time params for this call,
    // so that the APIRequest can be reused later on.
    String response = null;
    try {
      context.log("========Start of API Call========");
      response = executor.execute(method, getApiUrl(), getAllParams(extraParams), context);
      context.log("Response:");
      context.log(response);
      context.log("========End of API Call========");
    } catch(IOException e) {
      throw new APIException.FailedRequestException(e);
    }
    return response;
  }

  public APIContext getContext() {
    return this.context;
  }

  public void setContext(APIContext context) {
    this.context = context;
  }

  protected void setParamInternal(String param, Object value) {
    params.put(param, value);
  }

  protected void setParamsInternal(Map params) {
    this.params = params;
  }

  protected void requestFieldInternal(String field, boolean value) {
    if (returnFields == null) returnFields = new ArrayList();
    if (value == true && !returnFields.contains(field)) returnFields.add(field);
    else returnFields.remove(field);
  }

  private Map getAllParams(Map extraParams) {
    Map allParams = new HashMap(params);
    if (extraParams != null) allParams.putAll(extraParams);
    allParams.put("access_token", context.getAccessToken());
    if (context.hasAppSecret()) {
      allParams.put("appsecret_proof", context.getAppSecretProof());
    }
    if (returnFields != null) allParams.put("fields", joinStringList(returnFields));
    return allParams;
  }

  private static String readResponse(HttpsURLConnection con) throws APIException, IOException {
    try {
      int responseCode = con.getResponseCode();

      BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
      String inputLine;
      StringBuilder response = new StringBuilder();

      while ((inputLine = in.readLine()) != null) {
        response.append(inputLine);
      }
      in.close();
      return response.toString();
    } catch(Exception e) {

      BufferedReader in = new BufferedReader(new InputStreamReader(con.getErrorStream()));
      String inputLine;
      StringBuilder response = new StringBuilder();

      while ((inputLine = in.readLine()) != null) {
        response.append(inputLine);
      }
      in.close();
      throw new APIException.FailedRequestException(response.toString(), e);
    }
  }

  private String getApiUrl() {
    String endpointBas = useVideoEndpoint ? context.getVideoEndpointBase() : context.getEndpointBase();
    return endpointBas + "/" + context.getVersion() + "/" + nodeId + endpoint;
  }

  public static String joinStringList(List list) {
    if (list == null) return "";
    StringBuilder result = new StringBuilder();
    boolean isFirst = true;
    for (String s : list) {
      if (!isFirst) result.append(",");
      result.append(s);
      isFirst = false;
    }
    return result.toString();
  }

  private static String convertToString(Object input) {
    if (input == null) {
      return "null";
    } else if (input instanceof Map) {
      Gson gson = new GsonBuilder()
          .excludeFieldsWithModifiers(Modifier.STATIC)
          .excludeFieldsWithModifiers(Modifier.PROTECTED)
          .disableHtmlEscaping()
          .create();
      return gson.toJson((Map)input);
    } else if (input instanceof List) {
      Gson gson = new GsonBuilder()
          .excludeFieldsWithModifiers(Modifier.STATIC)
          .excludeFieldsWithModifiers(Modifier.PROTECTED)
          .disableHtmlEscaping()
          .create();
      return gson.toJson((List)input);
    } else {
      return input.toString();
    }
  }

  public APIRequest addToBatch(BatchRequest batch) {
    batch.addRequest(this);
    return this;
  }

  public APIRequest addToBatch(BatchRequest batch, String name) {
    batch.addRequest(name, this);
    return this;
  }

  BatchRequest.BatchModeRequestInfo getBatchModeRequestInfo() throws IOException {
    BatchRequest.BatchModeRequestInfo info = new BatchRequest.BatchModeRequestInfo();
    Map allParams = new HashMap(params);
    if (returnFields != null) allParams.put("fields", joinStringList(returnFields));
    info.method = this.method;
    StringBuilder relativeUrl = new StringBuilder(context.getVersion() + "/" + nodeId + endpoint);
    if (this.method.equals("POST")) {
      info.files = new HashMap();
      info.relativeUrl = relativeUrl.toString();
      StringBuilder body = new StringBuilder();
      boolean firstEntry = true;
      for (Map.Entry entry : allParams.entrySet()) {
        if (entry.getValue() instanceof File) {
          info.files.put((String)entry.getKey(), (File) entry.getValue());
        } else {
          body.append((firstEntry ? "" : "&") + URLEncoder.encode(entry.getKey().toString(), "UTF-8") + "=" + URLEncoder.encode(convertToString(entry.getValue()), "UTF-8"));
          firstEntry = false;
        }
      }
      info.body = body.toString();
    } else {
      boolean firstEntry = true;
      for (Map.Entry entry : allParams.entrySet()) {
        relativeUrl.append((firstEntry ? "?" : "&") + URLEncoder.encode(entry.getKey().toString(), "UTF-8") + "=" + URLEncoder.encode(convertToString(entry.getValue()), "UTF-8"));
        firstEntry = false;
      }
      info.relativeUrl = relativeUrl.toString();
    }
    return info;
  }

  public static interface ResponseParser {
    public APINodeList parseResponse(String response, APIContext context, APIRequest request)  throws APIException.MalformedResponseException;
  }

  public static interface IRequestExecutor {
    public String execute(String method, String apiUrl, Map allParams, APIContext context) throws APIException, IOException;
    public String sendGet(String apiUrl, Map allParams, APIContext context) throws APIException, IOException;
    public String sendPost(String apiUrl, Map allParams, APIContext context) throws APIException, IOException;
    public String sendDelete(String apiUrl, Map allParams, APIContext context) throws APIException, IOException;
  }

  public static class DefaultRequestExecutor implements IRequestExecutor {

    private static final String DEFAULT_CONTENT_TYPE = "application/octet-stream";
    public static Map fileToContentTypeMap = new HashMap();
    static {
      fileToContentTypeMap.put(".atom", "application/atom+xml");
      fileToContentTypeMap.put(".rss", "application/rss+xml");
      fileToContentTypeMap.put(".xml", "application/xml");
      fileToContentTypeMap.put(".csv", "text/csv");
      fileToContentTypeMap.put(".txt", "text/plain");
    }

    public String execute(String method, String apiUrl, Map allParams, APIContext context) throws APIException, IOException {
      if ("GET".equals(method)) return sendGet(apiUrl, allParams, context);
      else if ("POST".equals(method)) return sendPost(apiUrl, allParams, context);
      else if ("DELETE".equals(method)) return sendDelete(apiUrl, allParams, context);
      else throw new IllegalArgumentException("Unsupported http method. Currently only GET, POST, and DELETE are supported");
    }

    public String sendGet(String apiUrl, Map allParams, APIContext context) throws APIException, IOException {
      StringBuilder urlString = new StringBuilder(apiUrl);
      boolean firstEntry = true;
      for (Map.Entry entry : allParams.entrySet()) {
        urlString.append((firstEntry ? "?" : "&") + URLEncoder.encode(entry.getKey().toString(), "UTF-8") + "=" + URLEncoder.encode(convertToString(entry.getValue()), "UTF-8"));
        firstEntry = false;
      }
      URL url = new URL(urlString.toString());
      context.log("Request:");
      context.log("GET: " + url.toString());
      HttpsURLConnection con = (HttpsURLConnection) url.openConnection();

      con.setRequestMethod("GET");
      con.setRequestProperty("User-Agent", USER_AGENT);
      con.setRequestProperty("Content-Type","application/x-www-form-urlencoded");

      return readResponse(con);
    }

    public String sendPost(String apiUrl, Map allParams, APIContext context) throws APIException, IOException {
      String boundary = "--------------------------" + new Random().nextLong();
      URL url = new URL(apiUrl);
      context.log("Post: " + url.toString());
      HttpsURLConnection con = (HttpsURLConnection) url.openConnection();

      con.setRequestMethod("POST");
      con.setRequestProperty("User-Agent", USER_AGENT);
      con.setRequestProperty("Content-Type","multipart/form-data; boundary=" + boundary);
      con.setDoOutput(true);

      int contentLength = getContentLength(allParams, boundary, context);

      con.setRequestProperty("Content-Length", "" + contentLength);

      DataOutputStream wr = new DataOutputStream(con.getOutputStream());
      for (Map.Entry entry : allParams.entrySet()) {
        writeStringInUTF8Bytes(wr, "--" + boundary + "\r\n");
        if (entry.getValue() instanceof File) {
          File file = (File) entry.getValue();
          String contentType = getContentTypeForFile(file);
          writeStringInUTF8Bytes(wr, "Content-Disposition: form-data; name=\"" + entry.getKey() + "\"; filename=\"" + file.getName() + "\"\r\n");
          if (contentType != null) {
            writeStringInUTF8Bytes(wr, "Content-Type: " + contentType + "\r\n");
          }
          writeStringInUTF8Bytes(wr, "\r\n");
          FileInputStream fileInputStream = new FileInputStream(file);
          byte[] buffer = new byte[1024];
          int count = 0;
          while ((count = fileInputStream.read(buffer)) >= 0) {
            wr.write(buffer, 0, count);
          }
          writeStringInUTF8Bytes(wr, "\r\n");
          fileInputStream.close();
        } else if (entry.getValue() instanceof byte[]) {
          byte[] bytes = (byte[]) entry.getValue();
          writeStringInUTF8Bytes(wr, "Content-Disposition: form-data; name=\"" + entry.getKey() + "\"; filename=\"" + "chunkfile" + "\"\r\n\r\n");
          wr.write(bytes, 0, bytes.length);
          writeStringInUTF8Bytes(wr, "\r\n");
        } else {
          writeStringInUTF8Bytes(wr, "Content-Disposition: form-data; name=\"" + entry.getKey() + "\"\r\n\r\n");
          writeStringInUTF8Bytes(wr, convertToString(entry.getValue()));
          writeStringInUTF8Bytes(wr, "\r\n");
        }
      }
      writeStringInUTF8Bytes(wr, "--" + boundary + "--\r\n");

      wr.flush();
      wr.close();

      return readResponse(con);
    }

    public String sendDelete(String apiUrl, Map allParams, APIContext context) throws APIException, IOException {
      StringBuilder urlString = new StringBuilder(apiUrl);
      boolean firstEntry = true;
      for (Map.Entry entry : allParams.entrySet()) {
        urlString.append((firstEntry ? "?" : "&") + URLEncoder.encode(entry.getKey().toString(), "UTF-8") + "=" + URLEncoder.encode(convertToString(entry.getValue()), "UTF-8"));
        firstEntry = false;
      }
      URL url = new URL(urlString.toString());
      context.log("Delete: " + url.toString());
      HttpsURLConnection con = (HttpsURLConnection) url.openConnection();

      con.setRequestMethod("DELETE");
      con.setRequestProperty("User-Agent", USER_AGENT);

      return readResponse(con);
    }

    private static String getContentTypeForFile(File file) {
      String contentType = fileToContentTypeMap.get(getFileExtension(file));

      if (contentType != null) return contentType;

      try {
        contentType = Files.probeContentType(file.toPath());
      } catch (IOException ignored) {
      }

      return contentType != null ? contentType : DEFAULT_CONTENT_TYPE;
    }

    private static String getFileExtension(File file) {
      String fileName = file.getName();
      int index = fileName.lastIndexOf('.');
      if (index == -1) return "";
      return fileName.substring(index, fileName.length());
    }

    private static int getContentLength(Map allParams, String boundary, APIContext context) throws IOException {
      int contentLength = 0;
      for (Map.Entry entry : allParams.entrySet()) {
        contentLength += ("--" + boundary + "\r\n").length();
        if (entry.getValue() instanceof File) {
          File file = (File) entry.getValue();
          String contentType = getContentTypeForFile(file);
          contentLength += getLengthAndLog(context, "Content-Disposition: form-data; name=\"" + entry.getKey() + "\"; filename=\"" + file.getName() + "\"\r\n");
          if (contentType != null) {
            contentLength += getLengthAndLog(context, "Content-Type: " + contentType + "\r\n");
          }
          contentLength += getLengthAndLog(context, "\r\n");
          contentLength += file.length();
          contentLength += getLengthAndLog(context, "\r\n");
        } else if (entry.getValue() instanceof byte[]) {
          byte[] bytes = (byte[]) entry.getValue();
          contentLength += getLengthAndLog(context, "Content-Disposition: form-data; name=\"" + entry.getKey() + "\"; filename=\"" + "chunkfile" + "\"\r\n");
          contentLength += bytes.length;
          contentLength += getLengthAndLog(context, "\r\n");
        } else {
          contentLength += getLengthAndLog(context, "Content-Disposition: form-data; name=\"" + entry.getKey() + "\"\r\n\r\n");
          contentLength += getLengthAndLog(context, convertToString(entry.getValue()));
          contentLength += getLengthAndLog(context, "\r\n");
        }
      }
      contentLength += getLengthAndLog(context, "--" + boundary + "--\r\n");
      return contentLength;
    }

    private static int getLengthAndLog(APIContext context, String input) throws IOException {
      context.log(input);
      return input.getBytes("UTF-8").length;
    }

    private static void writeStringInUTF8Bytes(DataOutputStream wr, String input) throws IOException {
      wr.write(input.getBytes("UTF-8"));
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy