新年明けましておめでとうございます。今年もよろしくお願いいたします。私は今回の年末年始のお休みの間、なるだけPCを開かないでおこうと心に決めていたのですが、前々から気になっていたHerokuを少しだけ触ってみようかな、などど思い立ってしまいました。
さて、HerokuにはJava・Ruby on Rails・Python/Djangoなど、いくつかのWebアプリケーション実行環境が用意されています。JavaのWebアプリケーションを実行するために、Herokuの公式ドキュメントではJettyを使用していますが、ここはGlassfishを動作させてみましょう。jarファイル1個で起動できるEmbedded Glassfishを使用します。
Mavenを使ってビルドを行いますので、まずはpom.xmlを作成します。packagingはwarとし、maven-war-pluginには以下のようにweb.xml不要の設定をします。
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>2.3</version>
<configuration>
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>
さらに以下のdependencyを追加します。
<dependency>
<groupId>org.glassfish.main.extras</groupId>
<artifactId>glassfish-embedded-all</artifactId>
<version>3.1.2.2</version>
</dependency>
次にGlassfishを起動するクラスを作成します。リッスンポートは環境変数から取得するように設定しています。
public class Bootstrap {
public static void main(String[] args) {
try {
GlassFishProperties gfProps = new GlassFishProperties();
gfProps.setPort("http-listener",
Integer.parseInt(System.getenv("PORT")));
final GlassFish glassfish = GlassFishRuntime.bootstrap()
.newGlassFish(gfProps);
glassfish.start();
ScatteredArchive war = new ScatteredArchive("myApp",
ScatteredArchive.Type.WAR, new File("src/main/webapp"));
war.addClassPath(new File("target/classes"));
glassfish.getDeployer().deploy(war.toURI());
Runtime.getRuntime().addShutdownHook(new Thread(){
@Override
public void run() {
try {
System.out.println(glassfish + " shutdown now!!");
glassfish.dispose();
} catch (GlassFishException e) {
e.printStackTrace();
}
}
});
} catch (GlassFishException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
続いて、基本ということでサーブレットを作成しましょう。
@WebServlet(name = "MyServlet", urlPatterns = "/MyServlet")
public class MyServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req,
HttpServletResponse resp)
throws ServletException, IOException {
PrintWriter out = resp.getWriter();
out.println("Hello, Embedded-Glassfish!!");
}
@Override
public void init() throws ServletException {
System.out.println("init: " + this);
}
@Override
public void destroy() {
System.out.println("destroy: " + this);
}
}
一度ローカル環境でビルドして実行してみます。(クラスパス中、sample-projectの部分は適宜読み替えてください)
$ mvn clean package $ export PORT=18080 $ java -cp "target/classes:target/sample-project/WEB-INF/lib/*" Bootstrap
うまく起動できたら、別のコンソールを開いてサーバの応答を確認します。
$ curl "http://localhost:18080/myApp/MyServlet" Hello, Embedded-Glassfish!!
Herokuで稼働させるためには、もう1つファイルを追加する必要があります。プロジェクトのディレクトリ直下に"Procfile"という名前のテキストファイルを作成し、先ほどのjavaコマンドを記載します。
web: java -cp target/classes:target/sample-project/WEB-INF/lib/* Bootstrap
また、これは必須ではありませんがJDKのバージョンを指定したいときには、これもプロジェクトのディレクトリ直下、"system.properties"ファイルに以下の内容を記載します。
java.runtime.version=1.7
これで設定はできました。後はGitにcommitし、Herokuアプリケーションとしてpushするだけです。
$ git init $ git add . $ git commit -m "init" $ heroku create $ git push heroku master
pushすると、Heroku側でビルドが行われてアプリケーションが起動します。起動に成功していれば、コンソールに"http://xxxx-yyyy-8888.herokuapp.com deployed to Heroku"といった表示がされていますので、そのURLにアクセスしてみましょう。
$ curl "http://xxxx-yyyy-8888.herokuapp.com/myApp/MyServlet" Hello, Embedded-Glassfish!!
うまく動作しました。続けて何かを試してみる場合は、commitとpushを繰り返していけばプロジェクトの再ビルドとアプリケーションの再起動が行われます。使い終わったらアプリケーションを破棄します。
$ heroku destroy xxxx-yyyy-8888
深くは突っ込んでいないのですが、EJBやCDI、JSFも試してみた限り問題無く動作するようです(JPAは未確認)。ただし、どの程度の規模のアプリケーションまで正常稼働するのかはわかりません。もともとHeroku自体が軽量なWebアプリケーションをスケールして稼働させるものなので、あまり大掛かりなアプリケーションは載せようとしない方が良いとは思います。