はじめに (対象読者・この記事でわかること)
この記事は、JavaでWebアプリケーションを開発しており、Jettyサーバーを利用している開発者を対象としています。特に、JNDI(Java Naming and Directory Interface)の設定を追加した際に、ClassNotFoundException に遭遇してしまい、Jettyの起動に失敗する問題に直面している方に向けて書かれています。
この記事を読むことで、JNDI設定がJetty起動時の ClassNotFoundException を引き起こす根本的な原因を理解し、その具体的な解決策を習得することができます。これにより、JNDI設定を正しく適用し、Jettyサーバーを問題なく起動できるようになることを目指します。
前提知識
この記事を読み進める上で、以下の知識があるとスムーズです。 * Javaの基本的な文法とオブジェクト指向の概念 * Jettyサーバーの基本的な起動・停止方法 * JNDIの基本的な概念(データソースやJMSなどのリソースを名前で参照する仕組み)
Jetty起動時のClassNotFoundException:JNDI設定が招く落とし穴
Webアプリケーション開発において、データベース接続情報やメッセージキューなどの外部リソースの設定を、アプリケーションコードから分離するためにJNDIは非常に有効な仕組みです。Java EEコンテナでは標準的にサポートされていますが、スタンドアロンなServletコンテナであるJettyにおいても、JNDIの設定は可能です。
しかし、JettyでJNDIを利用しようとした際に、予期せぬ ClassNotFoundException に遭遇することがあります。このエラーは、JettyがJNDIに関連するクラスを見つけられない場合に発生します。原因は複数考えられますが、多くの場合、JNDIの設定ファイル(例: jetty.xml や web.xml)で指定されたリソースのクラスパスが正しく設定されていない、あるいはJNDI実装に必要なライブラリがJettyのクラスパスに含まれていないことに起因します。
例えば、特定のJDBCドライバーを指定してJNDIデータソースを定義した場合、そのJDBCドライバーのJARファイルがJettyのlibディレクトリに配置されていない、またはWebアプリケーションのWARファイル内に含まれていないと、Jettyはドライバークラスをロードできずに ClassNotFoundException を吐き出します。また、JNDIのネーミングサービス実装自体に必要なライブラリ(JNDI API自体はJDKに含まれていますが、LDAPなどの特定のサービスプロバイダーを利用する場合)が不足している可能性も考えられます。
この問題に直面すると、アプリケーションのデプロイやテストが滞り、開発のボトルネックとなることがあります。次章では、この ClassNotFoundException を解決するための具体的なアプローチと、それに伴う設定方法を詳細に解説していきます。
ClassNotFoundException の原因特定と解決策:JNDI設定の最適化
Jetty起動時にJNDI設定が原因で ClassNotFoundException が発生する問題は、主に以下の2つの側面からアプローチすることで解決できます。
- JNDIリソースに必要なクラスの配置: JNDIで定義したリソース(例: JDBCドライバー、JMSプロバイダー)のクラスファイルが、Jettyからアクセス可能なクラスパス上に存在しない。
- JNDI実装自体の依存関係: JNDIの特定の機能(例: LDAPネーミングサービス)を利用する場合、その実装に必要なライブラリが不足している。
これらの原因に対する具体的な解決策を、以下にステップバイステップで説明します。
JNDI設定とクラスパスの確認
JettyでJNDIを設定する一般的な方法として、etc/jetty.xml ファイルにリソース定義を追加する方法があります。例えば、PostgreSQLに接続するためのJDBCデータソースを定義する場合、以下のような設定が考えられます。
Xml<!-- etc/jetty.xml の例 --> <Configure class="org.eclipse.jetty.server.Server"> <New id="mydatasource" class="org.eclipse.jetty.plus.jndi.Resource"> <Arg>jdbc/mydatasource</Arg> <Arg type="javax.sql.DataSource"> <New class="org.postgresql.ds.PGSimpleDataSource"> <Set name="User"><Property name="db.user" default="postgres"/></Set> <Set name="Password"><Property name="db.password" default=""/></Set> <Set name="ServerName"><Property name="db.host" default="localhost"/></Set> <Set name="PortNumber"><Property name="db.port" default="5432"/></Set> <Set name="DatabaseName"><Property name="db.name" default="mydatabase"/></Set> </New> </Arg> </New> </Configure>
この設定例では、org.postgresql.ds.PGSimpleDataSource というクラスが使用されています。もし、このPostgreSQL JDBCドライバーのJARファイル(postgresql-XX.X.X.jar など)が、Jettyの lib ディレクトリや、Jettyが参照するクラスパス上のどこにも存在しない場合、Jettyは PGSimpleDataSource クラスを見つけられず、ClassNotFoundException が発生します。
解決策1: JDBCドライバーJARをJettyのlibディレクトリに配置する
最も簡単な解決策は、必要なJDBCドライバーのJARファイルをJettyの lib ディレクトリにコピーすることです。Jettyは、lib ディレクトリ内のJARファイルを自動的にクラスパスに追加します。
- JDBCドライバーJARの入手: PostgreSQL JDBCドライバーであれば、PostgreSQL公式サイトやMaven Centralなどからダウンロードできます。
- Jettyのlibディレクトリへの配置: ダウンロードしたJARファイルを、Jettyのインストールディレクトリにある
libディレクトリにコピーします。 例:$JETTY_HOME/lib/postgresql-XX.X.X.jar
これにより、Jettyは PGSimpleDataSource クラスをロードできるようになり、ClassNotFoundException が解消されるはずです。他のデータベース(MySQL, Oracleなど)を利用する場合も、同様にそれぞれのJDBCドライバーJARを lib ディレクトリに配置してください。
解決策2: WebアプリケーションのWEB-INF/libに配置する
JNDIリソースを特定のWebアプリケーションでのみ利用したい場合は、そのWebアプリケーションの WEB-INF/lib ディレクトリにJDBCドライバーJARを配置することも有効です。この場合、Jetty自体はドライバークラスを直接ロードしませんが、Webアプリケーションのクラスローダーがそれを見つけることができます。
- JDBCドライバーJARをWebアプリケーションの
WEB-INF/libにコピー: 例:your_webapp.war/WEB-INF/lib/postgresql-XX.X.X.jar
この方法も、Webアプリケーションが ClassNotFoundException を回避するのに役立ちます。
java.naming.factory.initial の設定とJNDIプロバイダー
JNDIは、名前空間にオブジェクトをバインドし、名前によってそれらのオブジェクトを検索するためのJava APIです。しかし、JNDI自体は抽象的なインターフェースであり、具体的なネーミングサービス(例: LDAP, RMI, CORBA)の実装は、別途「JNDIサービスプロバイダー」として提供されます。
ClassNotFoundException がJNDI API自体や、java.naming.factory.initial で指定した初期コンテキストファクトリクラスに関連する場合、そのサービスプロバイダーのJARファイルがクラスパス上に存在しないことが原因である可能性が高いです。
例えば、LDAPサーバーをJNDIのネーミングサービスとして利用したい場合、JNDI APIに加えて、LDAPサービスプロバイダーの実装(例: com.sun.jndi.ldap.LdapCtxFactory)を提供するJARファイル(通常はJDKに含まれていますが、環境によっては別途必要になる場合もあります)が必要です。
解決策3: 必要なJNDIサービスプロバイダーのJARを確認・配置する
JettyでJNDIのLDAPなどの特定のサービスを利用する場合、そのサービスプロバイダーの実装クラスを含むJARファイルがクラスパス上に存在するか確認してください。
- JDK標準のJNDI実装: 多くの基本的なJNDI機能(例: RMIレジストリ、メモリ内JNDI)はJDKに含まれています。JDKのバージョンによっては、LDAPなどの一部のサービスプロバイダーも含まれています。
- 外部ライブラリ: 特殊なJNDIサービス(例: 特定のLDAPサーバー向けプロバイダー)を利用する場合は、そのプロバイダーのJARファイルを別途入手し、Jettyの
libディレクトリやWebアプリケーションのWEB-INF/libに配置する必要があります。
JNDI設定の確認:
jetty.xml や web.xml でJNDIリソースを定義する際に、java.naming.factory.initial プロパティなどを明示的に設定している場合は、その値が正しいクラス名であることを確認し、そのクラスを提供するJARファイルがクラスパス上にあることを確認してください。
Xml<!-- 例: jetty.xml でLDAPファクトリを指定する場合 --> <Configure id="jndiContext" class="org.eclipse.jetty.plus.jndi.XmlConfiguration"> <Call name="bind"> <Arg>java:comp/env</Arg> <Arg> <New class="org.eclipse.jetty.plus.jndi.java.javaURLContextFactory"> <!-- LDAPサービスプロバイダーを使用する場合のプロパティ例 --> <Set name="environment"> <Map> <Entry key="java.naming.factory.initial" value="com.sun.jndi.ldap.LdapCtxFactory"/> <Entry key="java.naming.provider.url" value="ldap://localhost:389"/> </Map> </Set> </New> </Arg> </Call> </Configure>
この例で com.sun.jndi.ldap.LdapCtxFactory がロードできない場合は、LDAPサービスプロバイダーのJARが不足している可能性があります。
ハマった点とデバッグのヒント
JNDI関連の ClassNotFoundException は、原因がクラスパスの問題であることがほとんどですが、見落としやすい点もあります。
- Jettyの起動スクリプト/設定: Jettyの起動スクリプト (
start.jarなど) や、Javaのシステムプロパティでクラスパスがどのように設定されているかを確認することが重要です。 - 複数のJARファイル: 依存関係が複雑な場合、複数のJARファイルが原因で問題が発生することがあります。
jar -tf <jarfile>コマンドでJARファイル内のファイル一覧を確認したり、javap -classpath <classpath> <classname>コマンドでクラスがロードできるかテストしたりすると、原因特定に役立ちます。 - WebアプリケーションとJetty本体のクラスローダー: JettyとWebアプリケーションでは、クラスローダーの階層が異なります。Webアプリケーションの
WEB-INF/libにあるJARは、Webアプリケーションのクラスローダーからロードされます。Jetty本体のlibディレクトリにあるJARは、Jettyのクラスローダーからロードされます。JNDIリソースの定義がどちらのコンテキストでロードされるかに依存するため、配置場所を間違えるとClassNotFoundExceptionが発生します。
解決策4: 起動時のクラスパスを徹底的に確認する
Jettyの起動時に、JVMに渡されるクラスパス(java -cp または -classpath オプション)を詳細に確認してください。Jettyの start.jar を利用している場合、start.ini や start.d/*.ini ファイルでクラスパスが管理されています。これらの設定ファイルで、必要なJARファイルが網羅されているか確認してください。
また、Jettyのログ出力(通常、logs/jetty.log に出力されます)に、ロードできなかったクラスに関する詳細な情報が出力されているはずです。このログを注意深く確認することで、問題となっているクラス名や、そのクラスがどのJARファイルに含まれているべきかを特定する手がかりが得られます。
まとめ
Jetty起動時にJNDI設定が原因で ClassNotFoundException が発生する問題は、多くの場合、必要なライブラリ(JDBCドライバー、JNDIサービスプロバイダーなど)がJettyのクラスパス上に存在しないことに起因します。
- JDBCドライバー: データベース接続に必要なJDBCドライバーのJARファイルを、Jettyの
libディレクトリ、またはWebアプリケーションのWEB-INF/libに配置することで解決できます。 - JNDIサービスプロバイダー: LDAPなど、特定のJNDIサービスを利用する際に必要なプロバイダーのJARファイルも同様にクラスパスに配置する必要があります。
- クラスパスの確認: Jettyの起動スクリプトや設定ファイルで、Javaのクラスパスが正しく設定されているかを徹底的に確認することが、問題解決の鍵となります。
これらの解決策を適用することで、JNDI設定を正しく行い、Jettyサーバーを安定して起動させることができるはずです。
参考資料
