『Webアプリケーション構築入門』の郵便番号検索システムをAppFog上で動かす方法


ウェブアプリ構築の入門書である拙著『Webアプリケーション構築入門 第2版』のゴールは、Google Mapsを活用する郵便番号検索システムです。サーバ・サイドのプログラミング言語はJavaとPHPのうち、好きな方を選んで読み進められるようになっています。

ウェブアプリを構築できるようになると、こんどはそれを公開したくなるわけですが、本書の想定読者には学生も入っているので、「まずサーバを用意して・・・」のようなコストのかかる方法ではなく、基本無料の公開方法が必要です。

Java版をHerokuで公開する方法と、PHP版をPHP Fogで公開する方法を以前紹介したのですが、PHP Fogが今年いっぱいでサービスを終了し、AppFogに移行するらしいので、それにあわせてAppFogを試します。

PHP Fogでは、無料では機能が不十分なMySQLを避けてSQLiteを使いましたが、AppFogのMySQLは容量が100MBあるので、郵便番号検索のためには十分です(逆にSQLiteは使えなくなりました。使えれば、移植作業は楽だったのですが)。(参考:AppFog Pricing

作業は以下の通りです。(Ubuntu 12.10 Desktopで動作を確認しました。)

1. AppFogのアカウントを作る。

2. コマンドラインツールをインストールする。(参考:AF Command Line Tool)

sudo apt-get install ruby
sudo gem install af

3. ログインする。

af login

4. アプリを作成し、そこにMySQLサービスを追加、バインドする(ここ、かなり説明を端折っています)。

af bind-service {サービス名} {アプリ名}

5. データベースを準備する。

sudo apt-get install ruby-dev g++ mysql-client libssl-dev
sudo gem install caldecott
sudo gem install eventmachine -- --with-openssl-dir=/usr/lib/ssl
af tunnel

指示に従うとmysqlコマンドで接続できるようになります。あらかじめmysqldumpでデータベースをバックアップしておいて、以下のようなコマンドで復元します。

mysql> source {ファイル名}

6. ソースコードをアップロードする。

cd {コードのあるディレクトリ}
af update {アプリ名}

AppFogの流儀にあわせて、MySQLに接続する部分のコードを次のように書き換えておく必要があります(PHPのバージョンが比較的新しいので、接続時に文字コードを設定できます)。(参考:AppFog Docs MySQL)

//データベースに接続
$services_json = json_decode(getenv("VCAP_SERVICES"),true);
$mysql_config = $services_json["mysql-5.1"][0]["credentials"];
$user = $mysql_config["username"];
$pass = $mysql_config["password"];
$host = $mysql_config["hostname"];
$port = $mysql_config["port"];
$dbname = $mysql_config["name"];
$db = new PDO("mysql:host=$host;port=$port;dbname=$dbname;charset=utf8", $user, $pass);

実行結果は下のようになります。

『Webアプリケーション構築入門』の郵便番号検索システムをPHP Fog上で動かす方法


追記:PHP Fogは2012年12月にサービスを終了するそうなので、後継サービスであるAppFogを試しました。→『Webアプリケーション構築入門』の郵便番号検索システムをAppFog上で動かす方法

ウェブアプリ構築の入門書である拙著『Webアプリケーション構築入門 第2版』のゴールは、Google Mapsを活用する郵便番号検索システムです。サーバ・サイドのプログラミング言語はJavaとPHPのうち、好きな方を選んで読み進められるようになっています。

作成したウェブアプリを公開する方法は人によってさまざまでしょうが、PaaSの無料プランを使うのは魅力的な選択肢です。

Javaで作成したウェブアプリをHerokuというPaaSの上で動かす方法を以前紹介しました。(参照:『Webアプリケーション構築入門』のデータベースアプリケーションをHeroku上で動かす方法

ここでは、PHPで作成したウェブアプリをPHP FogというPaaSの上で動かす方法を紹介します。

手順は次のようになります(1と2は、PHP Fogを使い始めるときに1回だけやればOKです)。詳しくは、Jumpstartsあたりを参照してください。

  1. PHP Fogのアカウントを作る
  2. SSH鍵ファイルを登録する
  3. アプリを作る
  4. データベースを作る
  5. git clone URL
  6. アプリを作る
  7. git add .
  8. git commit -m 'コメント'
  9. git push

PHP FogではMySQLが使えますが、そのアクセス方法は、拙著で説明したのとは少し違っています。データベースへの接続情報は、環境変数に格納されているので、それを参照しながら接続します。参考までに、データベースに接続するためのコードを書くと、次のようになります。p. 149に掲載されているコードと比較してください。

//データベースに接続
$server = getenv('MYSQL_DB_HOST');
$username = getenv('MYSQL_USERNAME');
$password = getenv('MYSQL_PASSWORD');
$dbname=getenv('MYSQL_DB_NAME');

$db = new PDO("mysql:host=$server;dbname=$dbname", $username, $password,
                array(PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8'));

しかし、ここではこのコードは使いません。郵便番号データベースを構築するのが面倒だからです。拙著で紹介したように、テーブルを作成して郵便番号データのCSVファイルをインポートすれば郵便番号データベースを構築できるのですが、郵便番号データベースのためのCSVファイルは比較的大きいため、phpMyAdminでインポートしようとするとタイムアウトしてしまうのです。無料で利用できるデータベースサイズが5MBというのもいまいちです。Herokuのときもそうでしたが、無料の範囲内で遊ぼうとすると、データベース周りがいろいろ面倒です。

タイムアウトしてしまうという問題は、「split -C 500K /tmp/zip.csv」などとして分割し、「for f in x*; do mv $f $f.csv; done」として拡張子.csvを付けて、一つずつインポートすれば回避できますが、とても面倒です(Railsに行くのはここではがまんしましょう)。

こんなところで苦労するのはいやなので、Herokuの場合と同様に、SQLiteを組み込んで使うことにします。

ローカルでの実験のために、PHPからSQLiteを使えるようにしておきましょう。

sudo apt-get install php5-sqlite
sudo service apache2 restart

データベースファイル(mydb.sqlite)の作り方はHerokuの場合と同じなので省略します(完成したファイルはここにあります)。このファイルを、zips.phpと同じディレクトリに保存します(接続時にパスを指定するなら、別のディレクトリでもかまいません)。

zips.php内のデータベースに接続する部分を次のように修正します。

//データベースに接続
$db = new PDO('sqlite:mydb.sqlite');

Ajaxによって1文字入力するごとに検索結果を更新するようにしたものは、拙著の中では割愛しましたが(Java版とほとんど同じため)、サポートサイトでは掲載しているので参考にしてください。

実行結果は下のようになります。

『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>

『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を作ってください。