SQLite for Java on Heroku

拙著『Webアプリケーション構築入門 第2版』は、ウェブアプリ構築の入門書です。サーバ・サイドプログラムは、JavaとPHPで作成しており、読者は自分の好きな方を選んで読み進められるようになっています。

PHPで作ったウェブアプリはレンタルサーバとかで簡単に公開できるけど、Javaで作ったウェブアプリはちょっと面倒でした。

しかし、HerokuならJavaが動くので、これを使えば簡単です。このことについて、かつて書きました。

しかし、確かにJavaは動きますが、データベース管理システムは拙著で採用したMySQLではなくPostgreSQLなので、PostgreSQLへの対応が必要です。このことについて、かつて書きました。

しかし、拙著のサンプルアプリである郵便番号検索システムのためのデータベースは比較的大きく、ウェブアプリをPostgreSQLに対応させただけでは、データのインポートが面倒で使えません。この問題は、Postgres dev planという無料のアドオンで解決するのが簡単です。このことについて、かつて書きました。

しかし、これだけ書いた後で、「Postgres dev planでは、1つのテーブルに1万行までしか入れられないようにする予定です」というアナウンスがありました。郵便番号データベースには、14万行以上のデータがあるので、Postgres dev planではダメになるわけです。

月9ドルのBasic planに移行すれば問題は解決するのですが、そんなものを学生に勧められるわけもなく、もう面倒くさいのでSQLiteを組み込んで使うことにします。さらばPostgreSQL。

必要な作業は3つです。

  1. データベースファイルの作成
  2. SQLiteのためのライブラリの導入
  3. データベースへの接続方法の修正

データベースファイルの作成

FirefoxのアドオンであるSQLite Managerを使うのが簡単です。

  • アドオンをインストールする
  • ツール→SQLite Manager
  • pom.xmlがあるのと同じディレクトリにmydb.sqliteを作成する
  • デーブルを作る
  • 郵便番号データをインポートする

テーブルは次のようなSQL文を実行して作成します。

CREATE TABLE zip (
  code CHAR(7) NOT NULL,
  address1 VARCHAR(10) DEFAULT '' NOT NULL,
  address2 TEXT NOT NULL,
  address3 TEXT NOT NULL,
  address4 TEXT NOT NULL,
  office TEXT NOT NULL
);

CREATE INDEX code_idx ON zip (code)

データのインポートには、以前作成した/tmp/zip.csvを使えばいいでしょう。

SQLiteのためのライブラリの導入

pom.xmlに、次のような要素を追記します。

<dependency>
  <groupId>org.xerial</groupId>
  <artifactId>sqlite-jdbc</artifactId>
  <version>3.7.2</version>
</dependency>

データベースへの接続方法の修正

zips.jspを次のように修正します。

//データベースに接続
Class.forName("org.sqlite.JDBC");
Connection conn=DriverManager.getConnection("jdbc:sqlite:mydb.sqlite");

あとは『Webアプリケーション構築入門』のウェブアプリをHeroku上で動かす方法と同じです。


動作例

『Webアプリケーション構築入門』のデータベースアプリケーションをHeroku上で動かす方法


動作例

追記:無料で使えるHeroku Postgres Devは、行数が1万行に制限されることが発表されたので、別の方法(例:SQLiteを組み込む)を検討した方がいいでしょう。

何回かに分けて、拙著『Webアプリケーション構築入門 第2版』のJavaのウェブアプリをHeroku上で動かす方法を紹介してきました。

  1. MySQLからPostgreSQLへの移行
  2. Mavenの導入
  3. Herokuへのデプロイ

本質的な部分は以上で尽きているのですが、拙著の第9章で作成する郵便番号検索システム(Google Mapsとのマッシュアップ+Ajaxによるリアルタイム検索)を動作させるためには、Heroku上に郵便番号データベースを作らなければなりません。郵便番号データ程度の規模のデータをPaaSが無料(クレジットカード情報を入力せずに)で提供するデータベースにインポートするのは、面倒なことが多いのですが、Herokuも例外ではありません。ここでは、その方法を紹介します。

まず、Heroku Postgres Devを利用可能にします。本稿執筆時点で、このアドオンはクレジットカード情報を入力せずに利用できます。

以下のようなコマンドで、データベース情報を確認します。

heroku pg:info

この結果、データベース名が「HEROKU_POSTGRESQL_AQUA」だとわかったとします。そうしたら、以下のようなコマンドで、psqlで接続します(ファイアーウォールの中からだと接続できなくて、セキュアインターネット実験サービスでもだめだったりするから、後で何か考えないと・・・)。

heroku pg:psql HEROKU_POSTGRESQL_AQUA

テーブルを作り、データをインポートし、インデックスを張ります。に作成した/tmp/zip.csvをインポートしています。このファイルをHeroku側に転送しておく必要はありません。前に使った「COPY」は権限が無いため使えないので、代わりにpsqlのメタコマンドである「\copy」を使います。

CREATE TABLE zip (
  code CHAR(7) NOT NULL,
  address1 VARCHAR(10) DEFAULT '' NOT NULL,
  address2 TEXT NOT NULL,
  address3 TEXT NOT NULL,
  address4 TEXT NOT NULL,
  office TEXT NOT NULL
);

\copy zip FROM '/tmp/zip.csv' WITH CSV

CREATE INDEX code_idx ON zip.code;

\q

最初から使えるSHARED-DATABASEの代わりに、データベースをデフォルトに設定します。

heroku pg:promote HEROKU_POSTGRESQL_AQUA

これで郵便番号データベースは完成です。すでに紹介したアプリの配備方法を終わらせれば、拙著の郵便番号システムがHeroku上で動きます。

『Webアプリケーション構築入門』のウェブアプリをHeroku上で動かす方法

拙著『Webアプリケーション構築入門 第2版』では、PHPとJavaでウェブアプリを作る方法を解説しています。作ったウェブアプリを公開しようとするとき、PHPのウェブアプリはたいていのレンタルサーバで動くのでいいのですが、Javaのウェブアプリはほとんどのレンタルサーバでは動かないので、自分のサーバを持っていない場合はいろいろ面倒でした。

クラウド(IaaS)やVPSの普及によって、Javaのウェブアプリを公開するための経済的なハードルは下がりましたが、「レンタルサーバにファイルをコピーするだけでOK」というPHPに比べると、IaaSやVPSでの「サーバに各種サーバソフトウェアをインストールして・・・」という技術的ハードルはあまり下がりませんでした。

しかし最近(と言うほどでもありませんか)、HerokuのようなJavaも使えるクラウド(PaaS)の登場によって、Javaのウェブアプリを公開するための技術的ハードルも、ずいぶん下がってきたような気がします。

というわけで、拙著のJavaのウェブアプリを、できるだけ少ない作業で、クレジットカードを使わずにHerokuに移植する方法を紹介します。作業が必要な理由は以下の通りです。

  1. Herokuでは、Mavenプロジェクトを使うことが推奨されるから
  2. Herokuでは、無料で利用できるDBMSがMySQLではなくPostgreSQLだから
  3. Herokuでは、GlassFishではなく組み込みコンテナ(TomcatやJetty-Runner)を使うから
  4. Herokuには、Gitでプッシュしなければならないから

Getting Started with Herokuのstep 3までを済ませてから先に進んでください。

Maven

Mavenは、記事「『Webアプリケーション構築入門』のウェブアプリをMavenで管理する方法」のように導入できます。プロジェクトを作成したら、Herokuに不要な要素をpom.xmlから削除し、Commons Langを追加しておきましょう(この記事の最後に掲載したpom.xmlを参考に)。

PostgreSQLへの対応

追記:無料で使えるデータベースには、サイズや行数の制限があるので、郵便番号検索システムのためには別の方法(例:SQLiteを組み込む)を検討した方がいいでしょう。

PostgreSQLへの対応は、以前書いた「『Webアプリケーション構築入門』の郵便番号検索でPostgreSQLを使う方法(Javaの場合)」という記事で半分終わっています。JDBCを使えるようにし、データベースへの接続方法をHerokuにあわせれば完了です。

Mavenを使っているので、MySQLの場合と同様に、pom.xmlに以下のように記述すればJDBCが使えるようになります。拙著8.2.2項のように、ライブラリを手動で導入する必要はありません。

<dependency>
  <groupId>postgresql</groupId>
  <artifactId>postgresql</artifactId>
  <version>8.3-603.jdbc4</version>
</dependency>

Herokuでは、データベースへのアクセス情報を環境変数から取得することになっているので、データベースに接続する部分は次のように書き換えます(参照:Using the DATABASE_URL in plain JDBC)。

//データベースに接続
java.net.URI dbUri = new java.net.URI(System.getenv("DATABASE_URL"));
String username = dbUri.getUserInfo().split(":")[0];
String password = dbUri.getUserInfo().split(":")[1];
String dbUrl = "jdbc:postgresql://" + dbUri.getHost() + dbUri.getPath() + ":" + dbUri.getPort();
Connection conn = DriverManager.getConnection(dbUrl, username, password);

組み込みコンテナ

拙著では、ウェブアプリのコンテナとしてGlassFishを使っていますが、Herokuでは、組み込みのTomcatJettyを使います。ここでは、ソースコードの修正が不要なJetty Runnerを使いましょう。

maven-dependency-pluginの部分を次のように書き換えます。

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-dependency-plugin</artifactId>
  <version>2.4</version>
  <executions>
    <execution>
      <phase>package</phase>
      <goals>
        <goal>copy</goal>
      </goals>
      <configuration>
        <artifactItems>
          <artifactItem>
            <groupId>org.mortbay.jetty</groupId>
            <artifactId>jetty-runner</artifactId>
            <version>8.1.4.v20120524</version>
            <destFileName>jetty-runner.jar</destFileName>
          </artifactItem>
        </artifactItems>
      </configuration>
    </execution>
  </executions>
</plugin>

コンソールで次のように入力し、ビルドします。NetBeans上で、プロジェクト名を右クリック→「構築」としてもかまいません。PostgreSQLのデータベースを「mydb」、パスワードを「pass」と仮定しています。

cd ~/NetBeansProjects/プロジェクト名
mvn package

ローカルでの動作確認

ローカルでの動作確認は次のように行います。NetBeansからGlassFishを起動している場合は、出力タブの「GlassFish 3.1.2」等にある停止ボタンをクリックして、NetBeans上のGlassFishは止めておきます(あるいはGlassFishとは別のポートを指定する)。JSPがある場合は、JREのjavaではなく、JDKのjavaで実行しなければなりません(PATHの設定を変えるのが正しい方法です)。

export DATABASE_URL=postgres://postgres:pass@localhost/mydb
/usr/lib/jvm/java-7-openjdk-i386/bin/java -jar target/dependency/jetty-runner.jar --port 8080 target/*.war

http://localhost:8080/HelloServletのようなURLで、ウェブアプリにアクセス出来れば完成です。

NetBeansで実行するときもこのコードが動くようにするためには、上記の環境設定(DATABASE_URL)のもと、次のようにしてNetBeansを起動するといいでしょう(NetBeansのバージョンに依存します)。

~/netbeans-7.1.2/bin/netbeans &

Gitでプッシュする方法

次のような内容でウェブプロセスを定義するファイルProcfileを、pom.xmlがあるディレクトリに作ります。

web:    java $JAVA_OPTS -jar target/dependency/jetty-runner.jar --port $PORT target/*.war

Gitリポジトリを作成し、プロジェクトをインポートします(NetBeans上で、チーム→Git→初期化、チーム→Git→コミットとしてもかまいません)。

git init
echo target > .gitignore
git add .
git commit -m "Ready to deploy"

Herokuのアプリを作ります。

heroku create 好きな名前

NetBeansで作業する場合は、このときに表示されるGitリポジトリの場所、「git@heroku.com:名前.git」のような文字列が必要になります(Herokuのウェブサイトで確認できるので、記録する必要はありません)。

Herokuにプッシュします(NetBeansで、Git→リモート→プッシュとしてもかまいません。Gitリポジトリの場所を指定してください。このあたり、説明が雑で申し訳ないです)。

git push heroku master

次のような表示になれば成功です。

[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------

heroku open」とするとブラウザが起動するので、ウェブアプリが正しく動作することを確認してください。

うまく動作しないときは、「heroku logs」でログを確認するといいでしょう。

Herokuのプロセスは、「heroku ps」で確認できます。今の例では、次のような結果になるはずです。

=== web: `java $JAVA_OPTS -jar target/dependency/jetty-runner.jar --port $PORT target/*.war`
web.1: up for 4m

何も出力されていないときは、「heroku scale web=1」などとしてプロセスを起動してください。

すでにプッシュしたプロジェクトをNetBeansに新たに読み込むときは、チーム→Git→クローンとし、リポジトリURLと、Getting Started with Herokuで作成したはずの非公開鍵ファイル(~/.ssh/id_rsa)を指定してください。

最後に、この記事のために作成したpom.xmlを掲載しておきます。


<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.mycompany</groupId>
  <artifactId>*****</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>war</packaging>

  <name>*****</name>

  <dependencies>
    <dependency>
      <groupId>javax</groupId>
      <artifactId>javaee-web-api</artifactId>
      <version>6.0</version>
      <scope>provided</scope>
    </dependency>
    <dependency>
      <groupId>commons-lang</groupId>
      <artifactId>commons-lang</artifactId>
      <version>2.3</version>
    </dependency>
    <dependency>
      <groupId>postgresql</groupId>
      <artifactId>postgresql</artifactId>
      <version>8.3-603.jdbc4</version>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>2.3.2</version>
        <configuration>
          <source>1.6</source>
          <target>1.6</target>
        </configuration>
      </plugin>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-war-plugin</artifactId>
        <version>2.1.1</version>
        <configuration>
          <failOnMissingWebXml>false</failOnMissingWebXml>
        </configuration>
      </plugin>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-dependency-plugin</artifactId>
        <version>2.4</version>
        <executions>
          <execution>
            <phase>package</phase>
            <goals>
              <goal>copy</goal>
            </goals>
            <configuration>
              <artifactItems>
                <artifactItem>
                  <groupId>org.mortbay.jetty</groupId>
                  <artifactId>jetty-runner</artifactId>
                  <version>8.1.4.v20120524</version>
                  <destFileName>jetty-runner.jar</destFileName>
                </artifactItem>
              </artifactItems>
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>

</project>

Twitter4Jでつぶやく練習

拙著『Webアプリケーション構築入門』では、「Webアプリを作れるようになる前に、Webアプリを使えるようになるのが大事」という方針のもと、比較的早い段階で、JavaとPHP、JavaScriptでウェブAPIを利用する方法を紹介しています。

そこで採用したウェブAPIの一つがTwitter APIですが、入門書であることと、紙面が限られていることを理由に、OAuth認証なしでも使えるPublic Timelineだけしか紹介しませんでした。

OAuth認証を使ってつぶやく方法は、このブログで以前紹介しましたが(該当記事)、その後、Mavenを紹介したり(該当記事)、Twitterのサイト上で自分用のトークンを作れるようになったりしたので、「プログラムからつぶやく方法」を改めて書いておきましょう。

Twitterアプリの準備

  1. まず、Twitter DevelopersにTwitterアカウントでログインし、「Create a new app」をクリック、アプリケーションを登録します(コールバックURLはあとで変えられるので適当に入れておけばいいでしょう。今回は使いませんが)。
  2. つぶやくためにはWrite権限が必要なので、SettingsタブでApplication Typeを「Read & Write」に変更します。
  3. Detailsタブに戻って、「Create my access token」をクリックし、Access tokenとAccess token secretを生成します。このページに書かれているConsumer keyとConsumer secretも使います。

Mavenプロジェクトの作成

Mavenプロジェクトを作成し、pom.xmlのdependencies要素の中に次のように記述します(TwitterのためのライブラリであるTwitter4Jを使います)。(参考:『Webアプリケーション構築入門』のウェブアプリをMavenで管理する方法

<dependency>
  <groupId>org.twitter4j</groupId>
  <artifactId>twitter4j-core</artifactId>
  <version>[2.2,)</version>
</dependency>

つぶやくためのコード

つぎのようなコードでつぶやきます。

import twitter4j.*;
import twitter4j.conf.*;

public class Tweet {

  public static void main(String[] args) throws Exception {
    ConfigurationBuilder cb = new ConfigurationBuilder();
    cb.setDebugEnabled(true)
            .setOAuthConsumerKey(**Consumer key**)
            .setOAuthConsumerSecret(**Consumer secret**)
            .setOAuthAccessToken(**Access token**)
            .setOAuthAccessTokenSecret(**Access token secret**);
    TwitterFactory tf = new TwitterFactory(cb.build());
    Twitter twitter = tf.getInstance();
    twitter.updateStatus("テスト at " + (new java.util.Date()));
  }
}

『Webアプリケーション構築入門』のウェブアプリをMavenで管理する方法

拙著『Webアプリケーション構築入門』では、NetBeansやEclipseといったIDEの上でウェブアプリを開発する方法を紹介しています。IDEを使うので、ビルドプロセスは自動化されているわけですが、必要なライブラリの導入は手動ですし、機械的なテストは導入してもいません(紙面の限られた入門書であるため)。このあたりの欠点を補うためには、いろんなことを学ばなければなりませんが、ビルドやテスト、依存関係管理などを自動化するツールであるMavenを導入することは、その第一歩としてふさわしいものでしょう。

ここでは拙著のウェブアプリをMavenで管理する方法を紹介します。

NetBeansで新しいプロジェクト(Maven→Webアプリケーション)を作成し、Mavenのためのファイルpom.xmlの、<dependencies>から</dependencies>の間に、以下の要素を追記します。

<dependency>
  <groupId>commons-lang</groupId>
  <artifactId>commons-lang</artifactId>
  <version>2.3</version>
</dependency>
<dependency>
  <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
  <version>5.1.20</version>
</dependency>

これは、p. 86の作業「Apache Commons Lang」の導入と、8.2.2項(p. 131)の作業「MySQL JDBC ドライバの追加」に代わるものです。この記述があれば、ライブラリを追加する作業は不要になります。

必要な作業はこれだけです。あとは本書の通りにサーブレットやJSPを作ってください。