第一回J1チームで説明した内容のまとめ

先の準備手順の最後で作成したHello, Worldをベースに話を進めます。とはいえこのページの手順でも一番最初の手順でHelloWorldしますから、、EclipseのインストールとGoogle Plugin for Eclipseのインストールまでが終わっていれば問題無くこのページの内容を進める事が出来ます。

この日の説明の目的は「AppEngine上でBotを作るのに必要最低限な知識を説明する」事であり、Java的に良く無いとかAppEngine的に良く無いとか、そんな細かい事は気にせず進めました。

[宿題の復習]まずはプロジェクトを作成しよう

  1. まずは、Eclipseを起動します。
  2. 手順通りEclipseをインストールした人は "Project Explorerビュー"が左側に表示されているかもしれませんが、"Package Explorerビュー" を表示します。[Window][Show view][Package Explorer]で表示できます。
    1. エディタの下のビューに横方向に広く表示されていると不便なので、左側にドラッグしておきましょう。
  3. ツールバーのアイコンをクリックして新しくプロジェクトを作成します。
  4. 次の図のようなダイアログが表示されるので、プロジェクト名、パッケージ名、使用するGoogleモジュール、の設定を行います。
    プロジェクト名とパッケージ名は自分用の物を設定してください。また、今回は素のJavaだけを使いますので "User Google Web Toolkit"のチェックははずします。
  5. 最後にFinishボタンをクリックするとプロジェクトが完成します。
  6. とりあえず実行してみましょう。ツールバーのアイコンをクリックして、実行したいプロジェクトを選択すれば起動が開始されます。
  7. 以下のようなログが "Consoleビュー" に表示されれば起動完了です。

    2009-08-16 23:55:18.597 java[2277:80f] [Java CocoaComponent compatibility mode]: Enabled

    2009-08-16 23:55:18.598 java[2277:80f] [Java CocoaComponent compatibility mode]: Setting timeout for SWT to 0.100000

    The server is running at http://localhost:8080/


  8. ブラウザで "http://localhost:8080/" を開いてHello, Worldを確認しましょう。
  9. 確認が終わったらアプリケーションを停止しておきましょう。
ここまでは宿題の復習です。次からが本番。

Twitterを利用するのに便利なライブラリをプロジェクトに設定しよう

Java用の Twitterライブラリとしては一番メジャーだと思われる、twitter4j というライブラリを使用します。他にも今後ライブラリを使う必要が出てくる場合のために、ライブラリを登録する手順を説明します。

ライブラリをダウンロードしよう

  1. twitter4j のページを開き、"ダウンロード"の箇所にあるリンクをクリックして twitter4j-バージョン番号.zip をダウンロードします。
  2. ダウンロードした zip ファイルをローカルで解凍します。以下のようなファイルが解凍されている事を確認してください。
    • twitter4j-バージョン番号-javadoc.jar
    • twitter4j-バージョン番号-sources.jar
    • twitter4j-バージョン番号.jar
  3. これらの中の twitter4j-バージョン番号.jar というファイルをすぐに使用しますので解凍したフォルダを覚えておいてください。

ライブラリを自分のプロジェクトに追加しよう

  1. twitter4jのzipファイルをダウンロードして解凍した twitter4j-バージョン番号.jar を、Eclipseの "自分のプロジェクト/war/WEB-INF/lib" にコピー&ペーストしてください。この時、"Package Exploer" で作業している事を確認してから作業してください。
  2. コピーされた twitter4j-バージョン番号.jar を右クリックし、次の図のように[Build Path][Add to Build Path]をクリックします。
    "Package Explorer" ではなく "Project Explorerビュー"で作業していると、[Build Path]メニューが表示されないようなので注意してください。
  3. 上記の手順が正しく終了していれば、次の図のように "Referenced Libraries" に twitter4j-バージョン番号.jar が追加されているはずです。
これでTwitter4j が有効になりました。今後botを作る時に何かライブラリが必要であれば、以下の手順をやればおkって事です。
  1. 使用したいライブラリを war/WEB-INF/lib にコピーする。
  2. コピーしたライブラリを [Build Path][Add to Build Path] する。

TwitterのTimelineを表示してみよう

twitter4j を使って、適当なTimelineをHello, Worldの下に表示してみましょう。
  1. まずはプログラムをエディタで開きます。Package Explorerビューの src/com/shin1ogawa/BottukuServlet.java をダブルクリックするとEclipse のJavaEditor で開く事ができます。もちろん、人によってフォルダ名やプログラム名が変わりますので各自読み替えてください。
  2. エディタで開くと、Hello, World を表示しているプログラムが見えると思います。ここで以下のように入力します。今回は、Twitter4j の機能で特定のユーザのTimelineを取得する getUserTimeline("ユーザ名") という機能を使用する事にします。ちなみに、Twitter4jではTimelineにPostされているひとつひとつのつぶやきをStatusと呼びます。
    この手順で入力したいソースコードは以下のテキストボックスの内容です。下線付き太字の箇所が、追加する必要があるソースコードです。
    package com.shin1ogawa;

    import java.io.IOException;
    import java.util.Iterator;

    import javax.servlet.http.*;

    import twitter4j.Status;
    import twitter4j.Twitter;

    @SuppressWarnings("serial")
    public class BottukuServlet extends HttpServlet {
        public void doGet(HttpServletRequest req, HttpServletResponse resp)
                throws IOException {
            resp.setContentType("text/plain");
            resp.getWriter().println("Hello, world");
            
            Iterator<Status> iterator = new Twitter().getUserTimeline("gaetwbot").iterator();
        }
    }
    1. まずは以下の文を入力します。
      • Iterator<Status> iterator = new Twitter().getUserTimeline("gaetwbot").iterator();
    2. エディタを右クリックして[Source][Organize Imports]で import 文を自動入力させましょう。
      1. Status に対して選択肢が出ますので、twitter4j.Status を選択して下さい。
      2. 同様に Iterator には java.util.Iterator を選択して下さい。
      3. これで import 文は自動で入力されているはずです。
    3. ちなみに、eclipse の補完や便利なキーバインド等を使いつつ入力する場合の動画もあります。
  3. 次に、取得したStatusをHello, worldの次に出力してみましょう。getUserTimeline()で取得したStatusの一覧を次々とひとつづつ取得し、Hello, worldと同じように出力していきます。
    package com.shin1ogawa;

    import java.io.IOException;
    import java.util.Iterator;

    import javax.servlet.http.*;

    import twitter4j.Status;
    import twitter4j.Twitter;

    @SuppressWarnings("serial")
    public class BottukuServlet extends HttpServlet {
        public void doGet(HttpServletRequest req, HttpServletResponse resp)
                throws IOException {
            resp.setContentType("text/plain");
            resp.getWriter().println("Hello, world");
            
            Iterator<Status> iterator = new Twitter().getUserTimeline("gaetwbot").iterator();
            while(iterator.hasNext()) {
                Status status = iterator.next();
                resp.getWriter().println(status.getText());
            }
        }
    }
  4. ここまでの手順で出力できそうですが、Javaにはちょっと面倒な仕組みがあり、「TimeLineの取得をする時にエラーがあった場合にどうするか決めてくれなきゃ困る!」というような意味で赤い波線が表示されています。これを解決する必要があります。
    1. まずはこれまでに追加したソースコード4行を選択します。
    2. 4行が選択された状態で、エディタ上で右クリックをして[SurroundWith][Try/catch Block]を選択してください。成功すると次の図のように、選択していた箇所が try { .... } catch .... { ... } に囲まれて、赤い波線が消えているはずです。
    3. 赤い波線が一カ所もなければ保存して、アプリケーションを起動してブラウザで結果を確認してみてください。
      盛大に文字化けします。
    4. アプリケーションを停止して、以下の太字下腺のおまじないを追加して、赤い波線がなければ保存して再度動作確認をしてください。
      package com.shin1ogawa;

      import java.io.IOException;
      import java.util.Iterator;

      import javax.servlet.http.*;

      import twitter4j.Status;
      import twitter4j.Twitter;

      @SuppressWarnings("serial")
      public class BottukuServlet extends HttpServlet {
          public void doGet(HttpServletRequest req, HttpServletResponse resp)
                  throws IOException {
              resp.setContentType("text/plain");
              resp.setCharacterEncoding("utf-8");
              resp.getWriter().println("Hello, world");
              
              Iterator<Status> iterator = new Twitter().getUserTimeline("gaetwbot").iterator();
              while(iterator.hasNext()) {
                  Status status = iterator.next();
                  resp.getWriter().println(status.getText());
              }
          }
      }
Hello, world に続けて、目的のTimelineが文字化けせずに表示されていますね?

TwitterにPostしてみよう

次はTwitterへのPostを試してみます。先ほどはTimelineの取得だったので気にしませんでしたが、Postをするときは、Postを行うユーザアカウントで認証を行う必要があります。Twitter4jももちろん認証する為の機能を提供してくれています。
  1. 先ほどのソースコードの "new Twitter()"を選択状態にして、エディタを右クリックして[Quick fix]を選択してください。
    一番上に表示されている "Extract local variable(replace...)" を選択して(元々選択されていますが)Enterキーを押します。
  2. すると、次の図のような状態になります。
  3. この Twitter twitter = new Twitter(); を Twitter twitter = new Twitter("ユーザ名", "パスワード"); として下さい。安全のため、Twitterアカウントはbot用のアカウントを使いましょう。
  4. while(...) { ... } のすぐ下に、 "twitter.updateStatus("botからてすと");" と追加してみましょう(メッセージは好きなように変えてください)。updateStatus() という機能が、つぶやきをPostする機能です。以下の図のハイライトされている箇所が、TwitterにつぶやきをPostするために修正した箇所となります。
  5. アプリケーションを起動して動作確認してみましょう。ページを開いたと同時につぶやきがPostされるはずなので、アプリケーションでページを表示終わったら、つぶやいたアカウントのTimelineも確認してみてください。
    ※ページを何回もリロードしても、Twitter側で「前回と[ほぼ]同じつぶやきは無視する」という制限があるので、ひとつしかPostされないと思われます。
Twitter4jを使えば、つぶやきのPostも簡単だとわかりました。Timelineの取得も、getUserTimeline()以外にたくさんの種類があります。

AppEngineにデプロイしてみよう

ここまで来たら、AppEngine上にデプロイして動作を確認しましょう。TwitterにPostするメッセージは変えておいた方が良いです。
  1. war/WEB-INF/appengine-web.xmlをエディタで開いて、<application></application> 要素に、bot用のApplicationIDを設定して下さい。<version></version> は"1"のままでも構いませんが、これまでに一度でもデプロイした事がある人は変えておきましょう(以前のアプリケーションが上書きされてしまいます)。
  2. AppEngineにデプロイし、http:// deployした環境での動作確認しましょう。URLは、次のうちどちらかです。versionに1以外を指定した人は後者の方のURLを使うと良いです。
    1. http://アプリケーションID.appspot.com/
    2. http://バージョン.latest.アプリケーションID.appspot.com/

cron(定期的にプログラムを起動する)を設定してみよう

AppEngineにbotを設置するときは、自動的に動いてくれないと困ります。AppEngineには一定の間隔でプログラムを実行する機能があり、それを使ってみます。
  1. Package Explorerビューで war/WEB-INF を右クリックし、[New][Other...]を選択します。
  2. 以下のように、作成したcron.xmlを編集します。
    <?xml version="1.0" encoding="UTF-8"?>
    <cronentries>
      <cron>
        <url>/bottuku</url>
        <description>cron test.</description>
        <schedule>every 3 minutes</schedule>
      </cron>
    </cronentries>
    1. <url></url>には、作成したアプリケーションの相対パスを記述します。war/WEB-INF/web.xmlの servlet-mapping/url-pattern に記述されているものを記述して下さい。
    2. <description></description>は、管理コンソール上でcronの説明として表示されるものです。適当に入力して下さい。
    3. <schedule></schedule>は、どれだけの周期で起動するか?を指定します。上記の例では3分おきに実行されるような設定になっています。
  3. war/WEB-INF/cron.xmlが作成できたら、デプロイしてください。
    1. 先ほどまでのデプロイでは、最後の方が「Uploading index definitions.」「Deployment completed successfully」となっていましたが、今回はそのふたつのメッセージの間に「Uploading cron jobs.」というメッセージが表示されているはずです。
  4. ブラウザで http://appspot.com/ で管理コンソールを開き、左側の Cron Jobs を選択すると、次の図のように確認できるはずです。
  5. cronを止めたいときは、次のように <!-- --> で <cron></cron>を囲って保存し、デプロイすればおkです。
    <?xml version="1.0" encoding="UTF-8"?>
    <cronentries>
    <!--
      <cron>
        <url>/bottuku</url>
        <description>cron test.</description>
        <schedule>every 3 minutes</schedule>
      </cron>
    -->
    </cronentries>

AppEngineでも見る事ができるログを出力してみよう

ログを出力するには?

ローカルで実行しているときは Eclipse のコンソールに何かを出力できるのですが、AppEngineにデプロイしたアプリケーションが正常に動作しているか?や、プログラムのどこでエラーが発生したのか?を知る為に ログ を出力できると便利です。
  1. 以下のようにプログラムを編集します。
    static final Logger logger = Logger.getLogger(BottukuServlet.class.getName()); と入力し、これまでと同様にエディタを右クリックして[Source][Organize Imports]で import 文を自動入力させます。Logger について選択肢が出てきますので、java.util.logging.Logger を選択して下さい。
  2. ログを出力するなら、例えば次のようにします。
    この例だと、twitterの認証の前と、処理が全部終わった後の2カ所のタイミングでログを出力する事になります。
    1. logger.info("botが終了したよ") となっていますが、これは以下のような記述方法もできます。
      • logger.log(Level.INFO, "botが終了したよ");
      • Levelというのは、java.util.logging.Level をimportする必要があります。
    2. 上記の2種類の記述でinfoとなっているのはログの深刻度(レベル)の事です。深刻な順に記述すると、以下のような種類が存在します。
      1. SEVERE
      2. WARNING
      3. INFO
      4. CONFIG
      5. FINE
      6. FINER
      7. FINEST
    3. SEVEREで出力するときは logger.severe("") や logger.log(Level.SEVERE, "") のように書く事ができます。
  3. Pluginでプロジェクトを作成すると、どのレベル以上のログを出力するか?という設定ファイルに最初は「WARNING以上」という設定がされています。このままだと INFO レベルのログが無視されてしまうので、ログの設定ファイルを修正しましょう。
    1. war/WEB-INF/logging.properties を開きます。
    2. 次のように記述されている箇所を探します。
      • .level = WARNING
    3. これを次のように書き換えます。
      • .level = INFO
      • FINEやFINER,FINESTを指定しても構いません。
  4. logging.propertiesを設定して保存も出来たら、デプロイしてAppEngine上のアプリケーションを実行させてみます。
  5. すると、管理コンソールの左側の Logs を開いてログの出力結果を確認する事ができるはずです。

Datastoreに何かデータを保存したり取り出してみよう

保存する

例えば、bot用のTwiiterのユーザ名とパスワードを保存してみるなら次のようなソースコードを書きます。
  1. プログラムで、以下のように入力します。resp.getWriter().println("Hello, world"); の下くらいに書けば良いです。
    DatastoreService service = DatastoreServiceFactory.getDatastoreService();
    Entity entity = new Entity("Settings");
    entity.setProperty("username", "shin1ogawa");
    entity.setProperty("password", "xxxxx");
    service.put(entity);
    1. 例のごとく、import 文を自動入力するときの選択肢は以下です。
      • DatastoreService は com.gooogle.appengine.api.datastore.DatastoreService
      • Entity は com.gooogle.appengine.api.datastore.Entity
  2. ローカルでアプリケーションを起動してみましょう。
  3. http://localhost:8080/_ah/admin を開くと、Datastoreに保存されたかどうかを確認する事ができます。

取り出す

  1. 保存したデータを取り出すときは、次のようにします。
     DatastoreService service = DatastoreServiceFactory.getDatastoreService();
     Query query = new Query("Settings");
     Entity entity = service.prepare(query).asSingleEntity();
     String username = (String) entity.getProperty("username");
     String password = (String) entity.getProperty("password");
    1. Query は com.gooogle.appengine.api.datastore.Query を import します。
Datastoreに関しては当日もあまり詳しく説明していませんので、別途調べるか、MLで質問を投げてください。まじめに色々やろうとすると、結構大変だったりします。