2013年3月28日木曜日

Googleグループの共同トレイ

Google AppsのGoogleグループ機能について調べもの。

今まではGoogleグループ=メールエイリアス機能と言う程度の認識しかなかったが、 ここの説明によると共同トレイトピックという機能がありうまく使うとサポートセンタ的な使い方ができそうだというのを最近知った。

ただ、自分の使っている無料版Google Appsにはこれらの設定項目が全く出てこないので有料版のみの機能なのかもしれない。

それにしても公式のヘルプは、いまいちわかりづらい。

2013年3月16日土曜日

Twitter4JでStatusNetにアクセスする

前回の記事でTwitter風のマイクロブログStatusNetをWindows環境に立ち上たが、今回はこれにJavaプログラムからアクセスしてみる。 StatusNetはTwitter準拠のAPIを持っているとのことなのでJavaでTwitterにアクセスするためのライブラリTwitter4Jが使えないかと思い試してみることにした。

結果としては以下のようなコードでユーザーのタイムラインが取得できた。 StatusNetはBasic認証でも認証ができるので今回はその方法を使った。

import twitter4j.ResponseList;
import twitter4j.Status;
import twitter4j.Twitter;
import twitter4j.TwitterException;
import twitter4j.TwitterFactory;
import twitter4j.User;
import twitter4j.auth.BasicAuthorization;
import twitter4j.conf.Configuration;
import twitter4j.conf.ConfigurationBuilder;

public class TwitterConsole {
  
  public static void main(String[] args) {
    try {
      TwitterConsole app = new TwitterConsole();
      app.start();
    } catch(Throwable ex) {
       ex.printStackTrace(System.err);
    }
  }

    private void start() throws TwitterException {
        
    // StatusNetのAPIのBase URL
    String baseURL = "http://localhost/statusnet/api/";

    // Twitter4J設定。APIのBase URLをStatusNetのサーバーに変更
    ConfigurationBuilder cb = new ConfigurationBuilder();
    cb.setRestBaseURL(baseURL);
    cb.setIncludeEntitiesEnabled(true);
    cb.setJSONStoreEnabled(true);
    Configuration conf = cb.build() ;

    // Basic認証で認証する
    BasicAuthorization auth = new BasicAuthorization("username", "password");
    Twitter twitter = new TwitterFactory(conf).getInstance(auth);
    
    // ユーザーのタイムラインを取得
    ResponseList tweets = twitter.getHomeTimeline();

    // 取得したタイムラインを表示
    int i = 0;
    for(Status tweet : tweets) {
      i++;
      User tweetuser = tweet.getUser();
      System.out.printf("[%d] %s %s%n%s%n-----------%n",
          tweet.getId(),  // 投稿ID
          tweet.getCreatedAt().toString(),  // 投稿時刻
          tweetuser.getName(),  // 投稿者
          tweet.getText());     // 投稿内容
    }
  }
}

2013年3月14日木曜日

HttpClient4とMicrosoft Translator APIで翻訳する

前回の記事でAPIの呼び方を解説したが、その手順をJavaのプログラムにしてみた。 API呼び出しにはHttpClient4を使った。access_tokenの取得結果はJSONで返ってくるためその切り出しにはJSONICを使っている。

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import net.arnx.jsonic.JSON;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;


public class MSTranslatorTest {

  public static void main(String[] args) {
    try {
      MSTranslatorTest app = new MSTranslatorTest();
      app.translate();
    } catch(Throwable ex) {
      ex.printStackTrace(System.err);
    }
  }

  private void translate() throws IOException {
    DefaultHttpClient client = new DefaultHttpClient();
    
    // Step1 access_tokenを取得
    // 取得のためのリクエストを準備
    HttpPost httpPost = new HttpPost("https://datamarket.accesscontrol.windows.net/v2/OAuth2-13");
    List<NameValuePair> params = new ArrayList<NameValuePair>();
    params.add(new BasicNameValuePair("grant_type", "client_credentials"));
    // クライアント IDを設定
    params.add(new BasicNameValuePair("client_id", "...."));
    // 顧客の秘密を設定
    params.add(new BasicNameValuePair("client_secret", "...."));
    params.add(new BasicNameValuePair("scope", "http://api.microsofttranslator.com"));
    httpPost.setEntity(new UrlEncodedFormEntity(params));
    // 取得実行
    String accessToken;
    HttpResponse response1 = client.execute(httpPost);
    try {
      // 結果はJSONとして返ってくるのでJSONICでaccess_tokenを切り出す
      HttpEntity entity = response1.getEntity();
      String reponseText = EntityUtils.toString(entity);
      Map json = JSON.decode(reponseText, Map.class);
      accessToken = (String)json.get("access_token");
    } finally {
      httpPost.releaseConnection();
    }

    // Step2 翻訳する
    // 翻訳リクエストURLを作成
    // ・翻訳元=日本語 (from=ja)
    // ・翻訳先=スペイン語 (to=es)
    // ・翻訳する文字列 (text)
    String text = "こんにちは";
    String uri = String.format("http://api.microsofttranslator.com/V2/Http.svc/Translate?from=ja&to=es&text=%s", text);
    // access_tokenをヘッダに付与。"Bearer "を前につける。
    String authorization = String.format("Bearer %s", accessToken);
    HttpGet httpGet = new HttpGet(uri);
    httpGet.setHeader("Authorization", authorization);
    // 翻訳実行
    HttpResponse response2 = client.execute(httpGet);
    try {
      // 結果はXMLで返ってくる。そのままコンソールに表示。
      HttpEntity entity = response2.getEntity();
      String reponseText = EntityUtils.toString(entity);
      System.out.println(reponseText);
    } finally {
      httpGet.releaseConnection();
    }
  }
}

実行するとコンソールに次のような結果が表示される。

<string xmlns="http://schemas.microsoft.com/2003/10/Serialization/">¡Buenas noches!</string>

関連記事

2013年3月12日火曜日

マイクロブログStatusNetを立ち上げ

Twitterのようなメッセージと掲示板の合体したような仕組みをマイクロブログと呼ぶようだが、 オープンソースのマイクロブログのサーバーの一つにStatusNetというものがある。 TwitterというよりはYammerに近い感じであるが、今回Windows上に環境構築をしたのでそのメモ。

他にもオープンソースの有名どころとしてはSharetronixというのがありモバイル対応や機能が豊富そうであるが、ビジネスユースで使いにくそうな感じだったので今回はパス。

StatusNetはPHP+MySQLのWebアプリとして動作するため以下のものをセットアップする。 64bitのマシンを使っているので無謀にも64bitバイナリで揃えてみた。 (一応リンクを貼っておくが消える可能性もあるのでご注意)

インストール

ApacheとMySQLはインストーラーをつかいデフォルトの設定でインストールした。 PHPは展開し C:\Program Files\php-5.3.2-Win32-VC9-x64に移動。 StatusNetは展開してApacheのhtdocsフォルダ以下にstatusnetというフォルダとして配置した。

Apache設定

PHPを使えるようにする一般的な設定をすればよいと思う。 参考までにhttpd.confの以下を変更しセットアップした。 普通のやり方とちょっと違う点としては、一般的にはphp.iniはC:\Windowsに置くことが多いようだが、この環境ではPHPインストール先に置くこととした。
# LoadModuleを追加
LoadModule php5_module "C:/Program Files/php-5.3.2-Win32-VC9-x64/php5apache2_2.dll"
LoadModule php5_module "C:/Program Files/php-5.3.2-Win32-VC9-x64/php5ts.dll"

<Directory "C:/Program Files/Apache Software Foundation/Apache2.2/htdocs">
    ...
    # .htaccessを使えるようオーバーライドを許可しておく
    AllowOverride All
    ...
</Directory>

# index.phpを追加
<IfModule dir_module>
    DirectoryIndex index.html index.php
</IfModule>

# 拡張子.phpのmime type追加
<IfModule mime_module>
    ...

    AddType application/x-httpd-php .php
</IfModule>

# PHP INIを置くフォルダ。
PHPIniDir "C:/Program Files/php-5.3.2-Win32-VC9-x64"

PHPの設定

PHPを展開した先にphp.iniを作成する。同じフォルダにphp.ini-developmentがあるのでそれをコピーして以下を変更した。
output_handler = mb_output_handler

; エラーレベル設定
error_reporting = E_ALL & ~E_NOTICE

default_charset = "UTF-8"

; includeのパス
include_path = ".;c:\Program Files\php-5.3.2-Win32-VC9-x64\includes"

; ApacheのDocumentRoot
doc_root = "C:/Program Files/Apache Software Foundation/Apache2.2/htdocs"

; extension のパス
extension_dir = "C:\Program Files\php-5.3.2-Win32-VC9-x64\ext"

; 有効にするextension
extension=php_curl.dll
extension=php_gd2.dll
extension=php_mbstring.dll
extension=php_mysqli.dll

; mbstring関連設定
mbstring.language = Japanese
mbstring.internal_encoding = utf-8
mbstring.http_input = auto
mbstring.http_output = utf-8
mbstring.encoding_translation = On
mbstring.detect_order = auto
mbstring.substitute_character = none;
mbstring.func_overload = 0
エラーレベルは警告抑制のためである。既定のままだと "PHP Strict Standards: Non-static method PEAR::setErrorHandling() should not be called statically." のような警告が大量に出たため変更した。

MySQLデータベース作成

StatusNetのデータ記録用データベースを作成する。 データベース名はstatusnetとした。日本語を使いたいので文字セットはutf-8としておく。 MySQLのコンソールを立ち上げて次のコマンドで作成。
CREATE DATABASE statusnet CHARACTER SET utf8;
このデータベースのためのユーザーも作成。ユーザー名はstatusnet。パスワードはsql。
GRANT ALL PRIVILEGES ON statusnet.* TO statusnet@127.0.0.1 IDENTIFIED BY 'sql' WITH GRANT OPTION;

StatusNetの設定

展開したStatusNetのフォルダにあるhtaccess.sampleをコピーし.htaccessというファイルを作成する。 Windowsのエクスプローラーからは名前なしのファイルが作れないのでコマンドプロンプトからmoveコマンドなどでやるとよい。 このファイルの内容を一か所書き換える。以下の /mublog/となっているところを /statusnet/に書き換える。これをするとFancy URLsという機能が使えるようになる。
#RewriteBase /mublog/
RewriteBase /statusnet/

StatusNetのメインの設定はWeb画面より行う。 ApacheとMySQLが起動した状態でブラウザより http://localhost/statusnet/install.phpにアクセスすると設定画面が表示される。最低限以下の項目を入力しSubmitするとデータベースにテーブルが作成されセットアップが完了する。

Site settings - Site name
サイトの名前。何でもよい。
Database settings - Hostname
データベースサーバー名。localhostだとタイムアウトしてしまったので "127.0.0.1"を指定。
Database settings - Name
データベース名 "statusnet"
Database settings - DB username
データベースユーザー名 "statusnet"
Database settings - DB password
データベースパスワード "sql"
Administrator settings - Administrator nickname
StatusNet管理者のアカウント名
Administrator settings - Administrator nickname
StatusNet管理者のパスワード

Submit後に以下のようなデータベースのエラーで止まってしまうこともあるようである。


Adding notice source data to database...
ERROR (DB Error: unknown error) for SQL 'INSERT INTO notice_source (code, name, url, created) VALUES ('adium', 'Adium', ...
どうやらStatusNet1.1.0ではテーブルのセットアップスクリプトにバグがあるようである。 statusnet\db\notice_source.sql の内容をすべてコメントアウトすることでセットアップできるようである。 コメントアウトするとこのテーブルの初期データが入らないのだが実質は問題なく使用できるようである。 (たぶん通知関連のマスタデータかなにかではないかと思う) 一度このエラーが出てしまった場合には再実行の際に別のエラーが出るようなので、DBを一旦DROPし再作成してからするのが確実である。

とりあえず、ここまでできればStatusNetが使えるようになる…はず。 おつかれさまでした。

2013年3月7日木曜日

サーブレットで非同期処理

サーブレットで非同期処理をするためのAsyncContextというクラスがServlet3.0から追加されている。

そもそもサーブレットでは時間のかかる処理を書いたり、スレッドを作りバックグラウンドで処理をしてはいけないという決まりがあるらしい。しかしTomcatなど特にチェックなどしておらずやろうと思えばできてしまうため結構使っている人も多いのではないでしょうか?(自分もそうです)

そういう場合にServlet3.0からはAsyncContextを別スレッドを使って実行することができるようです。

2013年3月6日水曜日

PHPからMySQLへの接続ではまる

WindowsでPHPからmysqliでMySQL接続しようとしてはまった時のメモ。 以下の接続時にエラーが出た。
$mysqli = new mysqli("localhost", "user", "pass", "dbname");
エラーメッセージが文字化けして読めず何が悪いのかさっぱり不明。
Warning: mysqli::mysqli() [mysqli.mysqli]: [2002] ڑς݂̌Ăяo悪̎Ԃ߂ĂȂ (trying to connect via tcp://localhost:3306) in C:\Program Files\Apache Software Foundation\Apache2.2\htdocs\mysqli.php on line 6

Warning: mysqli::mysqli() [mysqli.mysqli]: (HY000/2002): ڑς݂̌Ăяo悪̎Ԃ߂ĂȂ߁Aڑł܂łB܂͐ڑς݂̃zXgȂ߁Amꂽڑ͎s܂B in C:\Program Files\Apache Software Foundation\Apache2.2\htdocs\mysqli.php on line 6
ネットで調べると「mysql.sockの設定がおかしいのでは」などの情報がたくさん出てくるがWindowsだとそれらしいものも無い。 とても悩んだが、どうもlocalhostがIPv6として解決されているのが原因のだったようである。 試しにIPアドレスに変えてみるとすんなり接続できた。
$mysqli = new mysqli("127.0.0.1", "user", "pass", "dbname");
確かにlocalhostにpingをかけてみるとIPv6アドレスを見に行っているようである。
C:\Temp>ping localhost

MyPC [::1]に ping を送信しています 32 バイトのデータ:
::1 からの応答: 時間 <1ms
::1 からの応答: 時間 <1ms
::1 からの応答: 時間 <1ms

こちらの情報を参考にさせていただきました。 こちらのページのようにhostsを変更して解決する方法もあるようです。