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

fluximpl.TwitterActionImpl Maven / Gradle / Ivy

The newest version!
package fluximpl;

import flux.*;
import flux.logging.Logger;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.scribe.builder.ServiceBuilder;
import org.scribe.builder.api.TwitterApi;
import org.scribe.model.*;
import org.scribe.oauth.OAuthService;
import org.scribe.utils.Preconditions;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

import static org.scribe.utils.StreamUtils.getStreamContents;

/**
 * Implementation for TwitterAction.
 *
 * @author [email protected]
 */
public class TwitterActionImpl extends ActionImpl implements TwitterAction {
  private static final String ACTION_VARIABLE = "TWITTER_ACTION_VARIABLE";
  private static final String AUTHORIZE_URL = "https://twitter.com/oauth/authorize";
  private static final String SEARCH_URL = "http://search.twitter.com/search.json";

  public TwitterActionImpl() {
    super(new FlowChartImpl(), "Twitter Action");
  }

  public TwitterActionImpl(FlowChartImpl fc, String name) {
    super(fc, name);
  }

  public Set getHiddenVariableNames() {
    // The returned raw type is a Set of String.
    @SuppressWarnings("unchecked")
    Set set = super.getHiddenVariableNames();
    set.add(ACTION_VARIABLE);
    return set;
  }


  public String getConsumerKey() {
    return getVariable().consumerKey;
  }

  @Override
  public void setConsumerKey(String apiKey) {
    TwitterVariable var = getVariable();
    var.consumerKey = apiKey;
    putVariable(var);
  }

  public int getCount() {
    return getVariable().count;
  }

  @Override
  public void setCount(int count) {
    TwitterVariable var = getVariable();
    var.count = count;
    putVariable(var);
  }

  public int getPage() {
    return getVariable().page;
  }

  @Override
  public void setPage(int page) {
    TwitterVariable var = getVariable();
    var.page = page;
    putVariable(var);
  }

  public String getConsumerSecret() {
    return getVariable().consumerSecret;
  }

  public Properties getParameters() {
    return getVariable().parameters;
  }

  @Override
  public void setParameters(Properties parameters) {
    TwitterVariable var = getVariable();
    var.parameters = parameters;
    putVariable(var);
  }

  public StringMapEntry getIndexedParameters(int index) {
    Properties bodyProperties = getVariable().parameters;
    return BodyPropertiesHelper.getIndexedBodyProperties(index, bodyProperties);
  }

  public StringMapEntry[] getIndexedParameters() {
    Properties queryParams = getVariable().parameters;
    return BodyPropertiesHelper.getIndexedBodyProperties(queryParams);
  }

  public void setIndexedParameters(StringMapEntry[] queryParameters) throws EngineException {
    TwitterVariable var = getVariable();
    Properties variableQueryParameters = var.parameters;
    BodyPropertiesHelper.setIndexedBodyProperties(queryParameters, variableQueryParameters);
    putVariable(var);
  }

  public void setIndexedParameters(int index, StringMapEntry arg) throws EngineException {
    TwitterVariable var = getVariable();
    Properties queryParams = var.parameters;
    BodyPropertiesHelper.setIndexedBodyProperties(index, arg, queryParams);
    putVariable(var);
  }

  @Override
  public void setConsumerSecret(String apiSecret) {
    TwitterVariable var = getVariable();
    var.consumerSecret = apiSecret;
    putVariable(var);
  }

  public boolean getOAuthEnabled() {
    return getVariable().oAuthEnabled;
  }

  @Override
  public void setOAuthEnabled(boolean oAuthEnabled) {
    TwitterVariable var = getVariable();
    var.oAuthEnabled = oAuthEnabled;
    putVariable(var);
  }

  public String getSearchQuery() {
    return getVariable().searchQuery;
  }

  @Override
  public void setSearchQuery(String searchQuery) {
    TwitterVariable var = getVariable();
    var.searchQuery = searchQuery;
    putVariable(var);
  }

  public RestActionType getRequestMethod() {
    return RestActionType.valueOf(getVariable().requestMethod);
  }

  @Override
  public void setRequestMethod(RestActionType actionType) {
    TwitterVariable var = getVariable();
    var.requestMethod = actionType.toString();
    putVariable(var);
  }

  public String getResourceUrl() {
    return getVariable().resourceUrl;
  }

  @Override
  public void setResourceUrl(String resourceUrl) {
    TwitterVariable var = getVariable();
    var.resourceUrl = resourceUrl;
    putVariable(var);
  }

  public String getUsername() {
    return getVariable().username;
  }

  @Override
  public void setUsername(String username) {
    TwitterVariable var = getVariable();
    var.username = username;
    putVariable(var);
  }

  public String getPassword() {
    Password password = getVariable().password;
    if (password != null) {
      return password.getEncryptedPassword();
    }
    return null;
  }

  @Override
  public void setPassword(String password) {
    TwitterVariable var = getVariable();
    if (password != null) {
      var.password = Password.makePassword(password);
    }
    putVariable(var);
  }

  private boolean isSearchRequest() {
    return getResourceUrl().equals(SEARCH_URL) && getRequestMethod().equals(RestActionType.GET);
  }

  @Override
  public Object execute(FlowContext flowContext) throws Exception {
    TwitterActionResult result;
    if (getOAuthEnabled()) {
      result = processOAuthRequest(flowContext);
    } else {
      Map queryParams = new HashMap();
      if (getCount() > 0) {
        queryParams.put("rpp", Integer.toString(getCount()));
      }
      if (getPage() > 0) {
        queryParams.put("page", Integer.toString(getPage()));
      }
      if (isSearchRequest()) {
        queryParams.put("q", getSearchQuery());
        for (Map.Entry entry : getParameters().entrySet()) {
          queryParams.put((String) entry.getKey(), (String) entry.getValue());
        }
        result = processRequest(flowContext, queryParams);
      } else {
        result = processRequest(flowContext, queryParams);
      }
    }
    return result;
  }

  private TwitterActionResult processRequest(FlowContext flowContext, Map queryParams) throws EngineException {
    Logger log = flowContext.getLogger();
    TwitterActionResult result = new TwitterActionResult();
    HttpURLConnection connection = null;
    try {
      connection = (HttpURLConnection) new URL(getResourceUrl()).openConnection();
      connection.setRequestMethod(getRequestMethod().name());
      String content = formURLEncodeMap(queryParams);
      log.info("Performing " + getRequestMethod().name() + " operation on " + getResourceUrl());
      if (!StringUtil.isNullOrEmpty(content)) {
        log.info("Encoded query params : " + content);
        connection.setRequestProperty("Content-Length", String.valueOf(content.getBytes().length));
        connection.setDoOutput(true);
        connection.getOutputStream().write(content.getBytes());
      }
      connection.connect();
      int code = connection.getResponseCode();
      log.info("Response status : " + code);
      String body = getStreamContents(connection.getInputStream());
      result.responseCode = code;
      result.response = body;
    } catch (Exception e) {
      log.severe("Failed to process request. " + e.getMessage());
    } finally {
      if (connection != null)
        connection.disconnect();
    }
    return result;
  }

  private TwitterActionResult processOAuthRequest(FlowContext flowContext) throws EngineException {
    Logger log = flowContext.getLogger();
    OAuthService service = new ServiceBuilder()
        .provider(TwitterApi.class)
        .apiKey(getConsumerKey())
        .apiSecret(getConsumerSecret())
//        .debug()
        .build();

    Token requestToken = service.getRequestToken();
    String oauthToken = requestToken.getToken();
    TwitterActionResult result = new TwitterActionResult();
    HttpURLConnection connection = null;
    try {
      //Authorize Step1: GET
      connection = (HttpURLConnection) new URL(AUTHORIZE_URL).openConnection();
      connection.setRequestMethod(Verb.GET.name());
      Map bodyParams = new HashMap();
      bodyParams.put("oauth_token", oauthToken);
      String content = formURLEncodeMap(bodyParams);
      connection.setRequestProperty("Content-Length", String.valueOf(content.getBytes().length));
      connection.setDoOutput(true);
      connection.getOutputStream().write(content.getBytes());
      // Perform Authorization using oauth_token
      connection.connect();
      int code = connection.getResponseCode();
      log.info("Authorize GET Response code : " + code);
      String body = getStreamContents(connection.getInputStream());
      log.finest("Authorize POST Response body : " + body);
      String authenticityToken = getAuthenticityTokenFromTwitterResponse(body);
      if (StringUtil.isNullOrEmpty(authenticityToken)) {
        throw new EngineException("Error authorizing consumer.");
      }
      //Authorize Step2: POST
      connection = (HttpURLConnection) new URL(AUTHORIZE_URL).openConnection();
      connection.setRequestMethod(Verb.POST.name());
      bodyParams = new HashMap();
      bodyParams.put("authenticity_token", authenticityToken);
      bodyParams.put("oauth_token", oauthToken);
      String password = getVariable().password.getClearTextPassword();
      bodyParams.put("session[password]", password);
      bodyParams.put("session[username_or_email]", getUsername());
      content = formURLEncodeMap(bodyParams);
      connection.setRequestProperty("Content-Length", String.valueOf(content.getBytes().length));
      connection.setDoOutput(true);
      connection.getOutputStream().write(content.getBytes());
      // Perform Authorize using authenticity_token, oauth_token, username, password
      connection.connect();
      code = connection.getResponseCode();
      log.info("Authorize POST Response code : " + code);
      body = getStreamContents(connection.getInputStream());
      log.finest("Authorize POST Response body : " + body);
      String pin = getPinFromTwitterResponse(body, log);
      Verifier verifier = new Verifier(pin);
      //Retrieve accessToken using verifier PIN.
      Token accessToken = service.getAccessToken(requestToken, verifier);
      String endpoint = null;
      Map queryParams = null;
      if (getRequestMethod().equals(RestActionType.GET)) {
        queryParams = new HashMap();
        for (Map.Entry entry : getParameters().entrySet()) {
          queryParams.put((String) entry.getKey(), (String) entry.getValue());
        }
        if (getCount() > 0) {
          queryParams.put("count", Integer.toString(getCount()));
        }
        if (getPage() > 0) {
          queryParams.put("page", Integer.toString(getPage()));
        }
        if (queryParams.size() > 0) {
          endpoint = getResourceUrl() + "?" + formURLEncodeMap(queryParams);
        } else {
          endpoint = getResourceUrl();
        }
      } else if (getRequestMethod().equals(RestActionType.POST)) {
        queryParams = new HashMap();
        for (Map.Entry entry : getParameters().entrySet()) {
          queryParams.put((String) entry.getKey(), (String) entry.getValue());
        }
        endpoint = getResourceUrl();
      }
      OAuthRequest request = new OAuthRequest(Verb.valueOf(getRequestMethod().name()), endpoint);
      for (Map.Entry entry : queryParams.entrySet()) {
        request.getBodyParams().add(entry.getKey(), entry.getValue());
      }
      service.signRequest(accessToken, request);
      log.info("Performing " + getRequestMethod().name() + " operation on " + endpoint);
      Response response = request.send();
      log.info("Response status : " + code);
      result.responseCode = response.getCode();
      result.response = response.getBody();
      log.finest("Response body : " + result.response);
    } catch (IOException e) {
      throw new EngineException("Error opening connection. Reason: " + e.getMessage());
    } finally {
      if (connection != null)
        connection.disconnect();
    }
    return result;
  }

  /**
   * Parses HTML response and fetches the authenticity token from Twitter.
   * A hacky way, but it works.
   *
   * @param html HTML response returned by Twitter.
   * @return authenticity token from Twitter.
   */
  private String getAuthenticityTokenFromTwitterResponse(String html) throws EngineException {
    Document document = Jsoup.parse(html, "UTF-8");
    Element element = document.body();
    try {
      element = element.select("div").select("form input:eq(0)").first();
    } catch (NullPointerException e) {
      throw new EngineException("Error retrieving authenticity token.");
    }
    return element.attr("value");
  }

  /**
   * Parses HTML response and fetches the verifier PIN from Twitter.
   * A hacky way, but works as long as Twitter response format remains same.
   *
   * @param html HTML response returned by Twitter.
   * @param log  Logger
   * @return verifier PIN from Twitter.
   * @throws flux.EngineException If an error occurs.
   */
  private String getPinFromTwitterResponse(String html, Logger log) throws EngineException {
    Document document = Jsoup.parse(html, "UTF-8");
    Element element = document.body();
    log.info("Response from Twitter : " + element.text());
    try {
      element = element.select("div:matches((?i)PIN)").last();
      element = element.getElementsByTag("code").first();
    } catch (NullPointerException e) {
      throw new EngineException("Error retrieving consumer PIN.");
    }
    return element.text();
  }

  @Override
  public void verify() throws EngineException {
    if (StringUtil.isNullOrEmpty(getRequestMethod().name())) {
      throw new EngineException("Expected \"Request method\" to be non-null or non-empty, but it was null or empty.");
    } // if
    if (getPage() < 0 || getCount() < 0) {
      throw new EngineException("Invalid page number or count.");
    }
    if (StringUtil.isNullOrEmpty(getResourceUrl())) {
      throw new EngineException("Expected \"Resource URL\" to be non-null or non-empty, but it was null or empty.");
    } // if
    if (isSearchRequest() && StringUtil.isNullOrEmpty(getSearchQuery())) {
      throw new EngineException("Expected \"Search query\" to be non-null or non-empty, but it was null or empty.");
    } // if
    if (getOAuthEnabled()) {
      if (StringUtil.isNullOrEmpty(getConsumerKey())) {
        throw new EngineException("Expected \"Consumer Key\" to be non-null or non-empty, but it was null or empty.");
      } // if
      if (StringUtil.isNullOrEmpty(getConsumerSecret())) {
        throw new EngineException("Expected \"Consumer Secret\" to be non-null or non-empty, but it was null or empty.");
      } // if
      if (StringUtil.isNullOrEmpty(getUsername())) {
        throw new EngineException("Expected \"Username\" to be non-null or non-empty, but it was null or empty.");
      } // if
      if (StringUtil.isNullOrEmpty(getPassword())) {
        throw new EngineException("Expected \"Password\" to be non-null or non-empty, but it was null or empty.");
      } // if
    }
  }

  private TwitterVariable getVariable() {
    if (!getVariableManager().contains(ACTION_VARIABLE)) {
      getVariableManager().put(ACTION_VARIABLE, new TwitterVariable());
    }
    return (TwitterVariable) getVariableManager().get(ACTION_VARIABLE);
  }

  private void putVariable(TwitterVariable variable) {
    getVariableManager().put(ACTION_VARIABLE, variable);
  }

  private static final String EMPTY_STRING = "";
  private static final String PAIR_SEPARATOR = "=";
  private static final String PARAM_SEPARATOR = "&";
  private static final String UTF_8 = "UTF-8";
  private static final String ERROR_MSG = String.format("Cannot find specified encoding: %s", UTF_8);

  public String formURLEncodeMap(Map map) {
    Preconditions.checkNotNull(map, "Cannot url-encode a null object");
    return (map.size() <= 0) ? EMPTY_STRING : doFormUrlEncode(map);
  }

  private String doFormUrlEncode(Map map) {
    StringBuilder encodedString = new StringBuilder(map.size() * 20);
    for (String key : map.keySet()) {
      encodedString.append(PARAM_SEPARATOR).append(formURLEncode(key));
      if (map.get(key) != null) {
        encodedString.append(PAIR_SEPARATOR).append(formURLEncode(map.get(key)));
      }
    }
    return encodedString.toString().substring(1);
  }

  private String formURLEncode(String string) {
    Preconditions.checkNotNull(string, "Cannot encode null string");
    try {
      return URLEncoder.encode(string, UTF_8);
    } catch (UnsupportedEncodingException uee) {
      throw new IllegalStateException(ERROR_MSG, uee);
    }
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy