はじめに (対象読者・この記事でわかること)

この記事は、JavaでWebアプリケーション開発を行っているエンジニア、特にGradleをビルドツールとして利用し、Herokuへデプロイしようとしている方を対象としています。
- Gradleで作成したServletアプリをHerokuにデプロイした際に、起動時に「java.lang.NoClassDefFoundError」や「Port binding error」などのエラーが出る原因が分かります。
- エラーログの読み方から、Procfile の書き方、system.properties の設定、そして依存ライブラリのスコープ調整まで、実際に動作する形にする具体的な手順が身につきます。
背景として、Heroku の無料プランでも簡単に Java アプリを試したいというニーズが増えている一方で、サーバレス環境特有の設定ミスが頻発している点があります。本記事はその壁を乗り越えるための実践ガイドです。

前提知識

この記事を読み進める上で、以下の知識があるとスムーズです。
- Java の基本文法と Servlet の概念
- Gradle のビルドスクリプト(build.gradle)の書き方
- Heroku の基本的な概念(Dyno、Procfile、環境変数)

Herokuデプロイの全体像とよくある問題点

Heroku は「git push だけでアプリが動く」ことが魅力ですが、Java アプリの場合は「JDK バージョン指定」や「ポートの取得」など、いくつかの必須設定があります。
1. JDK バージョン
デフォルトでは Heroku の Java Buildpack が使用する JDK が決まっており、ローカルと異なるとコンパイルエラーになることがあります。
2. ポート取得
Heroku は動的に割り当てたポート ($PORT 環境変数) でアプリを待ち受ける必要があります。Servlet コンテナ(Tomcat 等)にこのポートを渡さないと「address already in use」エラーが発生します。
3. 依存ライブラリのスコープ
provided スコープで宣言したライブラリはビルド時にだけ利用され、実行時には含まれません。Heroku のスラッグに含める必要があるものは runtime または compile に変更しないと ClassNotFoundException が起きます。

これらのポイントを踏まえて、実際のデプロイ手順とエラー対策を見ていきます。

実践手順:Gradle+Servlet アプリを Heroku にデプロイしエラーを解消する

以下では、ローカルで gradle build が通っている前提で、Heroku にデプロイするまでの一連の作業をステップごとに解説します。エラーが出たケースを中心に、原因と対策を具体的に示します。

ステップ1:プロジェクト構成と build.gradle の見直し

Groovy
plugins { id 'java' id 'war' // Servlet 用に war パッケージを生成 id 'org.gretty' version '4.0.3' // 開発時の組み込みサーバ (オプション) } group = 'com.example' version = '1.0.0' repositories { mavenCentral() } dependencies { // サーブレット API は provided にしておく (Heroku の dyno では自前で提供しない) providedCompile 'jakarta.servlet:jakarta.servlet-api:5.0.0' // 本番でも必要なライブラリは compile または implementation に implementation 'org.springframework.boot:spring-boot-starter-web:3.1.0' // テスト用 testImplementation 'junit:junit:4.13.2' } // War の作成時に依存ライブラリを埋め込む設定 war { from configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } } // Heroku 用に JDK バージョンを明示 task generateSystemProperties { doLast { file('system.properties').text = 'java.runtime.version=17' } } processResources.dependsOn generateSystemProperties
  • providedCompile を使用している理由は、Servlet コンテナ側で提供されるためです。ただし、Heroku の Dyno には Tomcat が自動で組み込まれないので、最終的に war にすべての依存を入れるか、runtime に変更します。
  • war タスクで runtimeClasspathwar に取り込む設定を入れると、ClassNotFoundException を防げます。

ステップ2:Procfile とポート取得コードの追加

Heroku ではアプリが起動するコマンドを Procfile に記述します。Servlet アプリの場合は、組み込みサーバ(例:Jetty、Tomcat)を直接起動させます。

Procfile

web: java -Dserver.port=$PORT -jar build/libs/yourapp-1.0.0.war
  • -Dserver.port=$PORT で Heroku が割り当てたポートをサーバに渡す。Spring Boot であれば自動的に取得できるが、純粋な Servlet の場合は自前で System.getenv("PORT") を取得し Connector に設定するコードが必要です。

サンプルコード(Embedded Jetty)

Java
public class Main { public static void main(String[] args) throws Exception { int port = Integer.parseInt(Optional.ofNullable(System.getenv("PORT")).orElse("8080")); Server server = new Server(port); WebAppContext ctx = new WebAppContext(); ctx.setContextPath("/"); ctx.setWar("src/main/webapp"); server.setHandler(ctx); server.start(); server.join(); } }

このコードを src/main/java/com/example/Main.java に追加し、war に含めます。

ステップ3:Heroku アプリ作成とデプロイ

Bash
# Heroku CLI がインストールされている前提 heroku create java-servlet-demo --buildpack heroku/java # Git リポジトリが未初期化なら git init git add . git commit -m "Initial commit" # デプロイ git push heroku master

デプロイ中に以下のようなビルドログが出ます。

-----> Java app detected
-----> Installing JDK 17... done
-----> Installing Maven... done
-----> Installing Gradle... done
-----> Compiling and packaging...

ハマった点やエラー解決

エラー1:java.lang.NoClassDefFoundError: jakarta/servlet/http/HttpServletRequest

  • 原因providedCompile にした jakarta.servlet-api が実行時に含まれていない。
  • 対策runtimeOnly に変更するか、war { from ... } でライブラリを埋め込む。
Groovy
runtimeOnly 'jakarta.servlet:jakarta.servlet-api:5.0.0'

エラー2:Address already in use (Port binding error)

  • 原因:サーバが固定ポート 8080 でリッスンしようとしている。Heroku では $PORT が必須。
  • 対策:コード内で System.getenv("PORT") を取得し、サーバに設定。Procfile でも -Dserver.port=$PORT を付与。

エラー3:Failed to download JDK version

  • 原因system.properties が無い、またはバージョン指定が古い。
  • 対策system.propertiesjava.runtime.version=17 を明示。Gradle タスク generateSystemProperties で自動生成すると便利。

エラー4:Gradle task 'bootRun' not found

  • 原因:Spring Boot プラグインが入っていないのに bootRun を実行しようとした。
  • 対策:Spring Boot であれば plugins { id 'org.springframework.boot' version '3.1.0' } を追加。Servlet 単体なら war タスクに集中すれば OK。

解決策のまとめ

  1. 依存ライブラリのスコープを見直す
    - providedCompileruntimeOnly または implementation に変更し、war に必ず同梱。
  2. ポート取得ロジックを実装
    - System.getenv("PORT") を取得し、サーバ起動時に設定。
  3. system.properties で JDK バージョンを固定
    - java.runtime.version=17 を明示し、Heroku のビルドエラーを防止。
  4. Procfile の記述を正しく
    - web: java -Dserver.port=$PORT -jar build/libs/yourapp-1.0.0.war の形式で、Dyno が自動的に web プロセスとして起動できるようにする。

以上の手順を踏めば、Gradle でビルドした Java Servlet アプリを Heroku に問題なくデプロイでき、実行時エラーを回避できます。

まとめ

本記事では、Gradle で構築した Java Servlet アプリを Heroku にデプロイした際に直面しやすい「依存ライブラリの欠如」「ポート取得失敗」「JDK バージョン不一致」などのエラーを具体例とともに解説し、以下のポイントで解決する手順を示しました。

  • 依存ライブラリは実行時に必ず含める(スコープ変更と war への埋め込み)
  • Heroku が割り当てるポートを取得してサーバに設定
  • system.properties で JDK バージョンを明示
  • 正しい Procfile 記述で Dyno が起動できるように

これにより、読者は Heroku に Java Servlet アプリをスムーズに展開でき、環境依存のトラブルを最小限に抑えることができます。次回は、データベース接続や環境変数を活用した本格的な Web アプリの構築方法について取り上げる予定です。

参考資料