2008/07/30 (Wed)

MixiAPIのテスト

atom/MixiTest.java

package atom;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.TimeZone;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.StringRequestEntity;

/**
 * @author tex
 * 
 */
public class MixiTest {

    private HttpClient client;

    public MixiTest() {
        this.client = new HttpClient();
    }

    public void post(String url, String username, String password)
            throws HttpException, IOException {
        PostMethod method = new PostMethod(url);
        method.addRequestHeader("X-WSSE",
                getWsseHeaderValue(username, password));
        String body = ""
                        + "<?xml version='1.0' encoding='utf-8'?>"
                        + "<entry xmlns='http://purl.org/atom/ns#'>"
                        + "  <title>Test</title>"
                        + "  <summary>Post diary from Mixi API</summary>"
                        + "</entry>";

        StringRequestEntity re =
                new StringRequestEntity(body, "application/atom+xml", "UTF-8");
        method.setRequestEntity(re);
        this.client.executeMethod(method);
        System.out.println(method.getStatusLine().toString());
        Header[] headers = method.getResponseHeaders();
        int len = headers.length;
        for (int i = 0; i < len; i++) {
            System.out.print(headers[i].toString());
        }
        System.out.println(method.getResponseBodyAsString());
    }

    protected final String getWsseHeaderValue(String username, String password) {
        try {
            byte[] nonceB = new byte[8];
            SecureRandom.getInstance("SHA1PRNG").nextBytes(nonceB);

            SimpleDateFormat zulu =
                    new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
            zulu.setTimeZone(TimeZone.getTimeZone("GMT"));
            Calendar now = Calendar.getInstance();
            now.setTimeInMillis(System.currentTimeMillis());
            String created = zulu.format(now.getTime());
            byte[] createdB = created.getBytes("utf-8");
            byte[] passwordB = password.getBytes("utf-8");

            byte[] v = new byte[nonceB.length
                    + createdB.length + passwordB.length];
            System.arraycopy(nonceB, 0, v, 0, nonceB.length);
            System.arraycopy(createdB, 0, v, nonceB.length, createdB.length);
            System.arraycopy(passwordB, 0, v, nonceB.length
                    + createdB.length, passwordB.length);

            MessageDigest md = MessageDigest.getInstance("SHA1");
            md.update(v);
            byte[] digest = md.digest();

            StringBuffer buf = new StringBuffer();
            buf.append("UsernameToken Username=\"");
            buf.append(username);
            buf.append("\", PasswordDigest=\"");
            buf.append(new String(Base64.encodeBase64(digest)));
            buf.append("\", Nonce=\"");
            buf.append(new String(Base64.encodeBase64(nonceB)));
            buf.append("\", Created=\"");
            buf.append(created);
            buf.append('"');
            return buf.toString();
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) throws Exception {
        MixiTest test = new MixiTest();
        test.post("http://mixi.jp/atom/diary/member_id=xxxx", "xxx@xxxxxxx",
                "xxxxxxxxxxxx");
    }
}

2008/02/16 (Sat)

簡単なHashの作り方。

RubyのHashを作るのは早くて簡単。

name = {"title", "mr", "first", "craig", "last", "davidson"}

Javaでは通常以下のようになる。

public void testBuildAHashNormally(){

     Map slow = new HashMap();
     slow.put("title", "mr");
     slow.put("first", "craig");
     slow.put("last", "davidson");

     assertEquals("mr", slow.get("title"));
     assertEquals("craig", slow.get("first"));
     assertEquals("davidson", slow.get("last"));
}

それを以下のようにしてみたい。

public void testBuildAHashQuickly(){

     Map quick = new Hash("title", "mr",
                "first", "craig", "last", "davidson");

     assertEquals("mr", slow.get("title"));
     assertEquals("craig", slow.get("first"));
     assertEquals("davidson", slow.get("last"));
}

それは以下のようにHashを拡張すればいい。

public class Hash extends HashMap {
   public Hash(Object ... keyValuePairs) {
     for (int i=0; i<keyValuePairs.length; i++)
       put(keyValuePairs[i], keyValuePairs[++i]);
   }
}

2008/01/29 (Tue)

Java Pretty Date

John Resig の記事のJavaScriptを移植した、ISO8601:2004の日付をParseするけれども、 commons-langを使うのが素直。

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;

//import org.apache.commons.lang.time.DateFormatUtils

public class PrettyDate {
    public static String prettyDate(String dateString) throws ParseException {
        if (dateString == null)
            return null;

        // Date date = DateFormatUtils.ISO_DATETIME_TIME_ZONE_FORMAT.format(date);
        Date date = parse(dateString);
        return prettyDate(date);

    }

    public static String prettyDate(Date date) throws ParseException {
        if (date == null) return null;
        Calendar cal = Calendar.getInstance();
        long diff = (cal.getTime().getTime() - date.getTime()) / 1000;
        double dayDiff = Math.floor(diff / 86400);

        if (dayDiff < 0 || dayDiff >= 31)
            return null;

        if (dayDiff == 0) {
            if (diff < 60   ) return "just now";
            if (diff < 120  ) return "1 minute ago";
            if (diff < 3600 ) return (int) Math.floor(diff / 60) + " minutes ago";
            if (diff < 7200 ) return "1 hour ago";
            if (diff < 86400) return (int) Math.floor(diff / 3600) + " hours ago";
        } else if (dayDiff == 1) {
            return "Yesterday";
        } else if (dayDiff < 7) {
            return (int) dayDiff + " days ago";
        } else if (dayDiff < 31) {
            return (int) Math.ceil(dayDiff / 7) + " weeks ago";
        }
        return null;

    }

    private static final SimpleDateFormat DATE_FORMAT_MILLIS_ISO8601 
            = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
    private static final String        REGEX_DATE_FORMAT_MILLIS_ISO8601 
    = "\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\.\\d{3}[-+]\\d{2}:\\d{2}";

    public static Date parse(String dateString) throws ParseException {
        Date date = null;
        if (dateString.matches(REGEX_DATE_FORMAT_MILLIS_ISO8601)) {
            int index = dateString.lastIndexOf(":");
            StringBuffer sb = new StringBuffer(dateString).deleteCharAt(index);
            date = DATE_FORMAT_MILLIS_ISO8601.parse(sb.toString());
        }
        return date;
    }

    public static void main(String[] args) {
        String[] dateStrings = { "2008-01-29T20:38:17.123+09:00",
                "2008-01-29T20:28:17.123+09:00",
                "2008-01-29T10:28:17.123+09:00",
                "2008-01-28T10:28:17.123+09:00",
                "2008-01-25T10:28:17.123+09:00",
                "2008-01-20T10:28:17.123+09:00", };
        try {
            for (String dateString : dateStrings) {
                System.out.println(PrettyDate.prettyDate(dateString));
            }
        } catch (ParseException e) {
            e.printStackTrace();
        }
    }
}

以下実行結果

prettyDate("2008-01-29T20:38:17.123+09:00")// =>21 minutes ago
prettyDate("2008-01-29T20:28:17.123+09:00")// =>31 minutes ago
prettyDate("2008-01-29T10:28:17.123+09:00")// =>10 hours ago
prettyDate("2008-01-28T10:28:17.123+09:00")// =>Yesterday
prettyDate("2008-01-25T10:28:17.123+09:00")// =>4 days ago
prettyDate("2008-01-20T10:28:17.123+09:00")// =>2 weeks ago