Getting Started with Java on Heroku/Cedar をためしてみる
先日HerokuでJavaが対応されたのと、さっそくためしてみてるブログに触発されたので実際にやってみる。
- Java がサポートされたクラウド環境 Heroku で Play!Scala を試す | アピリオ テックブログ (Appirio Tech Blog)
- 全くの初心者だが Heroku の Java サンプルを試してみた。 - 酒浸りの日々
とりあえず、Getting Started with Java on Heroku | Heroku Dev Centerをやってみる。
環境はMBA(SnowLeopard)。
Prerequisites
mavenとOpenJDK6とHerokuアカウントが必要な模様。
OpenJDK6
はじめから入っているのはJDK6(Apple)のはず。
$ java -version java version "1.6.0_26" Java(TM) SE Runtime Environment (build 1.6.0_26-b03-384-10M3425) Java HotSpot(TM) 64-Bit Server VM (build 20.1-b02-384, mixed mode)
なので、OpenJDKをMacPortsでインストール。
$ sudo port install openjdk6
1時間くらいかかったような・・・
で、バージョンを確認してみた。
$ /opt/local/share/java/openjdk6/bin/java -version openjdk version "1.6.0" OpenJDK Runtime Environment (build 1.6.0-b20) OpenJDK 64-Bit Server VM (build 17.0-b16, mixed mode)
OK。PATHとJAVA_HOMEを設定しておく。
Maven
インストールした覚えがないので、はじめからはいってたっぽい。
$ mvn -v Apache Maven 3.0.3 (r1075438; 2011-03-01 02:31:09+0900) Maven home: /usr/share/maven Java version: 1.6.0, vendor: Sun Microsystems Inc. Java home: /opt/local/share/java/openjdk6/jre Default locale: ja_JP, platform encoding: UTF-8 OS name: "darwin", version: "10.8.0", arch: "amd64", family: "unix"
ちなみにOpenJDKいれるまえは以下のような出力だった。
$ mvn -v Apache Maven 3.0.3 (r1075438; 2011-03-01 02:31:09+0900) Maven home: /usr/share/maven Java version: 1.6.0_26, vendor: Apple Inc. Java home: /System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home Default locale: ja_JP, platform encoding: SJIS OS name: "mac os x", version: "10.6.8", arch: "x86_64", family: "mac"
んー、OS nameやencodingが違うのはなぜ・・・?まぁ、とりあえずほっとくか。
Heroku アカウント
Heroku | Loginからアカウント作成する。
Local Workstation Setup
HerokuのコマンドラインクライアントとGitをインストールしなさい、もしこれまでHeroku使ったりしてたらスキップしてとのこと。
以前HerokuでRubyをためしたときにインストールしたのがコマンドラインクライアントと同じなのかな。
$ gem list heroku *** LOCAL GEMS *** heroku (2.6.1)
とりあえずherokuコマンドつかえるので、今回はスキップする。
ちなみに、インストーラを起動して、何がインストールされるのかなと確認したら、
- Foreman
- Git
- Heroku Client
がインストールされる模様。Foremanは・・・インストールされてないっぽい。コマンドにもgemにもない。とりえあえずそのままいってみる。
Gitは以前インストールしたのでこれを使う。ただし、バージョンは 1.7.6.1になってる。
Write Your App
サンプルコードを準備する
用意されているサンプルコードを src/main/java/HelloWorld.javaに作成する。
import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.*; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.servlet.*; public class HelloWorld extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.getWriter().print("Hello from Java!\n"); } public static void main(String[] args) throws Exception{ Server server = new Server(Integer.valueOf(System.getenv("PORT"))); ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); context.setContextPath("/"); server.setHandler(context); context.addServlet(new ServletHolder(new HelloWorld()),"/*"); server.start(); server.join(); } }
Jettyの組み込みWebサーバを使っているっぽい。こんなふうに書くんだなぁ。
mavenのpom.xmlを準備する
依存ライブラリを解決するために用意されているpom.xmlを書く。
<?xml version="1.0" encoding="UTF-8"?> <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/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.example</groupId> <version>1.0-SNAPSHOT</version> <artifactId>helloworld</artifactId> <dependencies> <dependency> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-servlet</artifactId> <version>7.4.5.v20110725</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.5</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>appassembler-maven-plugin</artifactId> <version>1.1.1</version> <executions> <execution> <phase>package</phase> <goals><goal>assemble</goal></goals> <configuration> <assembleDirectory>target</assembleDirectory> <generateRepository>false</generateRepository> <programs> <program> <mainClass>HelloWorld</mainClass> <name>webapp</name> </program> </programs> </configuration> </execution> </executions> </plugin> </plugins> </build> </project>
依存ライブラリはjettyとservlet-apiのみ。
あと、appassembler-maven-pluginというのを使うらしい。
.gitignoreにtargetを追加
mavenでビルドすると成果物がtargetディレクトリに作成されるのでここをgitの管理外にする。
ここまでやってディレクトリ構造はこんな感じ。
$ tree -a . ├── .gitignore ├── pom.xml └── src └── main └── java └── HelloWorld.java
Build Your App
ローカルでビルドする。といってもmavenなのでこれだけ。
$ mvn install
BUILD SUCCESSがでればOK。
ビルド後のディレクトリ構成はこんな感じ。
$ tree -a . ├── .gitignore ├── pom.xml ├── src │   └── main │   └── java │   └── HelloWorld.java └── target ├── bin │   ├── webapp │   └── webapp.bat ├── classes │   └── HelloWorld.class ├── helloworld-1.0-SNAPSHOT.jar ├── maven-archiver │   └── pom.properties └── surefire
target/binディレクトリがある。これがappassemblerで作成されるのか、へー。どうやらCLASSPATHに依存ライブラリも設定してくれるっぽい、ほー。うまく使うと便利そうだなぁ。
Declare Process Types With Foreman/Procfile
ビルドしたアプリを起動するのにProcfileというファイルが必要な模様。内容は1行のみ。
web: sh target/bin/webapp
どうやら、target/bin/webapp (以下ラッパースクリプト)を起動させるコマンドのようだ。
ラッパースクリプトを使うには、あらかじめ REPO 変数を定義しておく必要があるっぽい。ので定義しておく。
$ export REPO=$HOME/.m2/repository
ここまででディレクトリ構造はこんな感じ。Procfileが追加されただけ。
$ tree -a . ├── .gitignore ├── Procfile ├── pom.xml ├── src │   └── main │   └── java │   └── HelloWorld.java └── target ├── bin │   ├── webapp │   └── webapp.bat ├── classes │   └── HelloWorld.class ├── helloworld-1.0-SNAPSHOT.jar ├── maven-archiver │   └── pom.properties └── surefire
で、foremanで起動するのでインストールせよとのこと。
$ sudo gem install foreman
そして、foreman起動。
$ foreman start 02:49:23 web.1 | started with pid 77595 02:49:24 web.1 | 2011-09-14 02:49:24.508:INFO::jetty-7.4.5.v20110725 02:49:24 web.1 | 2011-09-14 02:49:24.640:INFO::started o.e.j.s.ServletContextHandler{/,null} 02:49:24 web.1 | 2011-09-14 02:49:24.689:INFO::Started SelectChannelConnector@0.0.0.0:5000 STARTING
で、http://localhost:5000/ にアクセス。Hello from Java!のメッセージを確認。OK。
foreman ってなにー?と思ったので調べてみたら以下がわかりやすかった。
どうやら、Procfileに記述した複数のプロセスを管理してくれるらしい。ログはコンソールにまとめて表示してくれるらしい。便利。
Store Your App in Git
herokuにデプロイするために、Gitのローカルリポジトリを作成しコミット。
$ git init $ git add . $ git commit -m "init"
Deploy to Heroku/Cedar
いよいよherokuにデプロイ。
まずは、Cedarスタックを作成。
$ heroku create --stack cedar
アプリのURLとherokuのgitのURLが表示される。
また、herokuのリモートリポジトリもローカルリポジトリに登録されるので、そのままherokuへpushしてデプロイ。
$ git push heroku master
pushが完了したらmavenのビルドが走った。
コマンドは以下の模様。
.maven/bin/mvn -B -Duser.home=/tmp/build_2y56vwsxf0tss -s .m2/settings.xml -DskipTests=true clean install
テストはスキップするみたい。当たり前か。
アプリのURLを叩くと、先ほどと同じメッセージが表示された!