拙著『Webアプリケーション構築入門 第2版』では、PHPとJavaでウェブアプリを作る方法を解説しています。作ったウェブアプリを公開しようとするとき、PHPのウェブアプリはたいていのレンタルサーバで動くのでいいのですが、Javaのウェブアプリはほとんどのレンタルサーバでは動かないので、自分のサーバを持っていない場合はいろいろ面倒でした。
クラウド(IaaS)やVPSの普及によって、Javaのウェブアプリを公開するための経済的なハードルは下がりましたが、「レンタルサーバにファイルをコピーするだけでOK」というPHPに比べると、IaaSやVPSでの「サーバに各種サーバソフトウェアをインストールして・・・」という技術的ハードルはあまり下がりませんでした。
しかし最近(と言うほどでもありませんか)、HerokuのようなJavaも使えるクラウド(PaaS)の登場によって、Javaのウェブアプリを公開するための技術的ハードルも、ずいぶん下がってきたような気がします。
というわけで、拙著のJavaのウェブアプリを、できるだけ少ない作業で、クレジットカードを使わずにHerokuに移植する方法を紹介します。作業が必要な理由は以下の通りです。
- Herokuでは、Mavenプロジェクトを使うことが推奨されるから
- Herokuでは、無料で利用できるDBMSがMySQLではなくPostgreSQLだから
- Herokuでは、GlassFishではなく組み込みコンテナ(TomcatやJetty-Runner)を使うから
- 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では、組み込みのTomcatやJettyを使います。ここでは、ソースコードの修正が不要な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>