GAE + GWT プログラミングメモ

Google App Engine とGoogle Web Toolkit のメモ

Twitter APIを利用するためのJavaコード

JavaでTwitter REST API v1.1のリクエスト - GAE + GWT プログラミングメモ
で書いたコードを整理した。

Twitterクラス

public class Twitter {
    private static String USERS_URL = "https://api.twitter.com/1.1/users/";
    private static String SHOW_URL = USERS_URL + "show.json";
    private static String SEARCH_URL = USERS_URL + "search.json";
    private static String OAUTH_CONSUMER_KEY = "your consumer key";
    private static String OAUTH_ACCESS_TOKEN = "your access token";
    private static String OAUTH_CONSUMER_SECRET = "your consumer secret";
    private static String OAUTH_ACCESS_TOKEN_SECRET = "your access token secret";
    private static OAuth oAuth = new OAuth(
        OAUTH_CONSUMER_KEY,
        OAUTH_ACCESS_TOKEN,
        OAUTH_CONSUMER_SECRET,
        OAUTH_ACCESS_TOKEN_SECRET);

    public String getUsersShow(String screen_name) throws InvalidKeyException,
            NoSuchAlgorithmException, IOException {
        Map<String, String> urlParamMap = new HashMap<String, String>();
        urlParamMap.put("screen_name", screen_name);

        return oAuth.getJson(SHOW_URL, urlParamMap);
    }

    public String getUsersSearch(String query) throws InvalidKeyException,
            NoSuchAlgorithmException, IOException {
        String encodedQuery = URLEncoder.encode(query, "UTF-8");
        Map<String, String> paramMap = new HashMap<String, String>();
        paramMap.put("q", encodedQuery);

        return oAuth.getJson(SEARCH_URL, paramMap);
    }
}

GETしか実装していないが、必要になったらPOSTもその内実装するかも。
OAuth部分を抜き出して、Twitter用の処理を分離した。

OAuthクラス

OAuth部分の抜き出し。
使い方は、コンストラクタで、Consumer Key、Access token等を設定し、getJsonの引数で、URLとURL Parameterを指定する。

コンストラクタ

public class OAuth {
    private static String HMAC_SHA1 = "HMAC-SHA1";
    private static String OAUTH_CONSUMER_KEY_KEY = "oauth_consumer_key";
    private static String OAUTH_NONCE_KEY = "oauth_nonce";
    private static String OAUTH_SIGNATURE_METHOD_KEY = "oauth_signature_method";
    private static String OAUTH_TIMESTAMP_KEY = "oauth_timestamp";
    private static String OAUTH_TOKEN_KEY = "oauth_token";
    private static String OAUTH_VERSION_KEY = "oauth_version";
    private static String OAUTH_SIGNATURE_KEY = "oauth_signature";

    private String oAuthConsumerSecret;
    private String oAuthAccessTokenSecret;
    private Map<String, String> oAuthParamMap = new HashMap<String, String>();

    public OAuth(String oAuthConsumerKey, String oAuthAccessToken,
            String oAuthConsumerSecret, String oAuthAccessTokenSecret) {
        super();
        this.oAuthConsumerSecret = oAuthConsumerSecret;
        this.oAuthAccessTokenSecret = oAuthAccessTokenSecret;
        createOAuthParamMap(oAuthConsumerKey, oAuthAccessToken);
    }

    private void createOAuthParamMap(String oAuthConsumerKey,
            String oAuthAccessToken) {
        long millis = System.currentTimeMillis();
        String oAuthNonce = String.valueOf(millis);
        String oAuthSignatureMethod = HMAC_SHA1;
        String oAuthTimestamp = getTimestamp(millis);
        String oAuthVersion = "1.0";

        oAuthParamMap.put(OAUTH_CONSUMER_KEY_KEY, oAuthConsumerKey);
        oAuthParamMap.put(OAUTH_NONCE_KEY, oAuthNonce);
        oAuthParamMap.put(OAUTH_SIGNATURE_METHOD_KEY, oAuthSignatureMethod);
        oAuthParamMap.put(OAUTH_TIMESTAMP_KEY, oAuthTimestamp);
        oAuthParamMap.put(OAUTH_TOKEN_KEY, oAuthAccessToken);
        oAuthParamMap.put(OAUTH_VERSION_KEY, oAuthVersion);
    }

    private String getTimestamp(long millis) {
        long secs = millis / 1000;
        return String.valueOf(secs);
    }
}

createOAuthParamMapでOAuth認証に必要となるパラメーターをMapに準備している。

getJson実装

public class OAuth {
    private static String GET_METHOD = "GET";
    private static String AUTHORIZATION = "Authorization";

    public String getJson(String url, Map<String, String> paramMap)
            throws IOException, InvalidKeyException, NoSuchAlgorithmException {
        String urlWithParams = getUrlWithParams(url, paramMap);
        URLConnection urlConnection = new URL(urlWithParams).openConnection();
        String authorization = getAuthorizationValue(GET_METHOD, url, paramMap);
        urlConnection.setRequestProperty(AUTHORIZATION, authorization);

        StringWriter writer = new StringWriter();
        IOUtils.copy(urlConnection.getInputStream(), writer);

        return writer.toString();
    }
}

urlとparamMapからgetUrlWithParamsでリクエストURLを作成する。
また、getAuthorizationValueで認証のためのValue値を作成し、setRequestPropertyする。

OAuthクラスの実装

public class OAuth {
    private static String AUTHORIZATION = "Authorization";
    private static String UTF8 = "UTF-8";
    private static String HMAC_SHA1 = "HMAC-SHA1";
    private static String OAUTH_CONSUMER_KEY_KEY = "oauth_consumer_key";
    private static String OAUTH_NONCE_KEY = "oauth_nonce";
    private static String OAUTH_SIGNATURE_METHOD_KEY = "oauth_signature_method";
    private static String OAUTH_TIMESTAMP_KEY = "oauth_timestamp";
    private static String OAUTH_TOKEN_KEY = "oauth_token";
    private static String OAUTH_VERSION_KEY = "oauth_version";
    private static String OAUTH_SIGNATURE_KEY = "oauth_signature";
    private static String GET_METHOD = "GET";

    private String oAuthConsumerSecret;
    private String oAuthAccessTokenSecret;
    private Map<String, String> oAuthParamMap = new HashMap<String, String>();

    public OAuth(String oAuthConsumerKey, String oAuthAccessToken,
            String oAuthConsumerSecret, String oAuthAccessTokenSecret) {
        super();
        this.oAuthConsumerSecret = oAuthConsumerSecret;
        this.oAuthAccessTokenSecret = oAuthAccessTokenSecret;
        createOAuthParamMap(oAuthConsumerKey, oAuthAccessToken);
    }

    private void createOAuthParamMap(String oAuthConsumerKey,
            String oAuthAccessToken) {
        long millis = System.currentTimeMillis();
        String oAuthNonce = String.valueOf(millis);
        String oAuthSignatureMethod = HMAC_SHA1;
        String oAuthTimestamp = getTimestamp(millis);
        String oAuthVersion = "1.0";

        oAuthParamMap.put(OAUTH_CONSUMER_KEY_KEY, oAuthConsumerKey);
        oAuthParamMap.put(OAUTH_NONCE_KEY, oAuthNonce);
        oAuthParamMap.put(OAUTH_SIGNATURE_METHOD_KEY, oAuthSignatureMethod);
        oAuthParamMap.put(OAUTH_TIMESTAMP_KEY, oAuthTimestamp);
        oAuthParamMap.put(OAUTH_TOKEN_KEY, oAuthAccessToken);
        oAuthParamMap.put(OAUTH_VERSION_KEY, oAuthVersion);
    }

    private String getTimestamp(long millis) {
        long secs = millis / 1000;
        return String.valueOf(secs);
    }

    public String getJson(String url, Map<String, String> paramMap)
            throws IOException, InvalidKeyException, NoSuchAlgorithmException {
        String urlWithParams = getUrlWithParams(url, paramMap);
        URLConnection urlConnection = new URL(urlWithParams).openConnection();
        String authorization = getAuthorizationValue(GET_METHOD, url, paramMap);
        urlConnection.setRequestProperty(AUTHORIZATION, authorization);

        StringWriter writer = new StringWriter();
        IOUtils.copy(urlConnection.getInputStream(), writer);

        return writer.toString();
    }

    private String getUrlWithParams(String url, Map<String, String> urlParamMap) {
        if (urlParamMap.isEmpty()) {
            return url;
        } else {
            return url + "?" + createParamString(urlParamMap);
        }
    }

    private String getAuthorizationValue(String method, String url,
            Map<String, String> paramMap) throws InvalidKeyException,
            NoSuchAlgorithmException, UnsupportedEncodingException {
        StringBuffer buffer = new StringBuffer("OAuth ");
        for (Entry<String, String> entry : oAuthParamMap.entrySet()) {
            buffer.append(entry.getKey());
            buffer.append("=\"" + entry.getValue() + "\"");
            buffer.append(", ");
        }

        String oAuthSignature = computeSignature(method, url, paramMap);
        buffer.append(OAUTH_SIGNATURE_KEY);
        buffer.append("=\"" + oAuthSignature + "\"");

        return buffer.toString();
    }

    private String getSignatureBase(String method, String url,
            Map<String, String> paramMap) throws UnsupportedEncodingException {
        TreeMap<String, String> sortedParamMap = new TreeMap<String, String>();
        sortedParamMap.putAll(paramMap);
        sortedParamMap.putAll(oAuthParamMap);

        String paramString = createParamString(sortedParamMap);

        String signatureBase =
            URLEncoder.encode(method, UTF8)
                + "&"
                + URLEncoder.encode(url, UTF8)
                + "&"
                + URLEncoder.encode(paramString, UTF8);

        return signatureBase;
    }

    private String createParamString(Map<String, String> paramMap) {
        TreeMap<String, String> treeMap = new TreeMap<String, String>(paramMap);
        StringBuffer paramStringBuffer = new StringBuffer();
        for (Entry<String, String> paramEntry : treeMap.entrySet()) {
            if (!paramEntry.equals(treeMap.firstEntry())) {
                paramStringBuffer.append("&");
            }
            paramStringBuffer.append(paramEntry.getKey()
                + "="
                + paramEntry.getValue());
        }
        return paramStringBuffer.toString();
    }

    private String computeSignature(String method, String url,
            Map<String, String> paramMap) throws NoSuchAlgorithmException,
            InvalidKeyException, UnsupportedEncodingException {
        SecretKey secretKey = null;

        String compositeKey =
            URLEncoder.encode(oAuthConsumerSecret, UTF8)
                + "&"
                + URLEncoder.encode(oAuthAccessTokenSecret, UTF8);
        byte[] keyBytes = compositeKey.getBytes();
        secretKey = new SecretKeySpec(keyBytes, "HmacSHA1");

        Mac mac = Mac.getInstance("HmacSHA1");

        mac.init(secretKey);

        String signatureBase = getSignatureBase(method, url, paramMap);
        byte[] text = signatureBase.getBytes();
        String signature =
            new String(Base64.encodeBase64(mac.doFinal(text))).trim();

        return URLEncoder.encode(signature, UTF8);
    }
}