Tomcat(またはJNDIリソースを同じように管理するアプリケーションサーバー)でJNDIリソースに接続するためにHibernateを使用している場合、このチュートリアルはあなたのためにあります!
背景として、TomcatがアプリケーションがJNDIリソースにアクセスできるかどうかを判断する方法の1つは、コンテキストクラスローダをチェックすることです。 コンテキストクラスローダがWebアプリケーションクラスローダまたはWebアプリケーションクラスローダの子である場合、TomcatはそのクラスローダにJNDIリソースをアクセスさせることができます。
Liferay Digital Experience Platform(DXP)アプリケーションはTomcat上で展開し、DXPのクラスローダーはWebアプリケーションクラスローダーですが、DXPポートレットクラスローダーはそうではなく、ポートレットはOSGiクラスローダーを使用してLiferayのOSGiコンテナにインストールされたOSGiバンドルです。 ポートレットがTomcat上でアプリケーションに提供されるJNDIリソースにアクセスするためには、ポートレットがDXPのクラスローダの子であるクラスローダに切り替える必要があります。
このチュートリアルでは、Hibernateの構成がJNDIデータソースを活用できるように、コンテキスト・クラスローダーの切り替えを行う方法を説明します。
注: ここで示したコードは、Tomcatで動作しますが、他のアプリケーションサーバーでの動作を保証するものではありません。 JNDIリソースへのアクセスを管理する方法については、アプリケーションサーバーのドキュメントを参照してください。
環境
このコードは、Liferay DXP SP3から利用できる改良を活用しています。
解像度
まずHibernateの構成記述子で、JNDIデータソースを使用するセッションファクトリを指定します。 以下は省略した説明文です:
<?xml version="1.0"?>
<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="connection.datasource">java:comp/env/jdbc/mydb</property>
<property name="current_session_context_class">thread</property>
// Specify resources here ...
</session-factory>
</hibernate-configuration>
上記の記述子をベースにする場合、 connection.datasource プロパティ値をJNDIデータソース名に置き換え、セッションで使用するリソースを指定することを確認してください。
次に、 HibernateUtil クラスは、DXPのクラスローダーを使用するHibernateセッションを作成および管理するためのメソッドを提供します。 注、 // カスタマイズ START/END コメントは、Hibernateの設定を処理する際に、呼び出し側がJNDIデータソースにアクセスできるようにするコードをマークします。
import com.liferay.portal.kernel.log.Log;
import com.liferay.portal.kernel.log.LogFactoryUtil;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
// Customization START
import com.liferay.portal.kernel.util.PortalClassLoaderUtil;
import com.liferay.portal.kernel.util.AggregateClassLoader;
// Customization END
public class HibernateUtil {
public static final String COUNT_COLUMN_NAME = "COUNT_VALUE";
public static void closeSession(Session session) {
try {
if ((session != null) && session.isOpen()) {
session.close();
}
}
catch (HibernateException he) {
_log.error(he.getMessage());
}
}
public static String getCountColumnName() {
return COUNT_COLUMN_NAME;
}
public static SessionFactory getSessionFactory() {
return _instance._sessionFactory;
}
public static Session openSession() throws HibernateException {
return openSession(getSessionFactory());
}
public static Session openSession(SessionFactory sessionFactory)
throws HibernateException {
return sessionFactory.getCurrentSession();
}
private HibernateUtil() {
// CUSTOMIZATION START
Thread thread = Thread.currentThread();
ClassLoader threadClassLoader = thread.getContextClassLoader();
ClassLoader portalClassLoader = PortalClassLoaderUtil.getClassLoader();
ClassLoader hibernateClassLoader =
AggregateClassLoader.getAggregateClassLoader(
portalClassLoader, threadClassLoader);
thread.setContextClassLoader(hibernateClassLoader);
// CUSTOMIZATION END
try {
Configuration configuration = new Configuration();
configuration = configuration.configure();
_sessionFactory = configuration.buildSessionFactory();
}
catch (Exception e) {
_log.error(e, e);
}
// CUSTOMIZATION START
finally {
thread.setContextClassLoader(threadClassLoader);
}
// CUSTOMIZATION END
}
private static Log _log = LogFactoryUtil.getLog(HibernateUtil.class);
private static HibernateUtil _instance = new HibernateUtil();
private SessionFactory _sessionFactory;
}
このクラスのロジックを分解してみましょう。 まず、その静的メンバが一番下で宣言されています。
private static Log _log = LogFactoryUtil.getLog(HibernateUtil.class);
private static HibernateUtil _instance = new HibernateUtil();
private SessionFactory _sessionFactory;
ここでは、各メンバーについて説明します:
-
_log: このクラスのメッセージをログに記録します。 -
_instance:HibernateUtilのインスタンスです。 -
_sessionFactory: 設定されると、JNDIリソース(例えば、JNDIデータソース)にアクセスすることを望むHibernateセッションを管理するものです。
コンストラクタ HibernateUtil() が "魔法 "をかけてくれる:
-
現在のスレッド、そのコンテキストのクラスローダー(OSGiのクラスローダーである)、DXPのクラスローダーを取得します。
Thread thread = Thread.currentThread(); ClassLoader threadClassLoader = thread.getContextClassLoader(); ClassLoader portalClassLoader = PortalClassLoaderUtil.getClassLoader(); -
DXPのクラスローダー(Portalクラスローダー)の子で、OSGiバンドル用に作成したOSGiクラスローダーからクラスと設定にアクセスできる新しいクラスローダーを作成する。
AggregateClassLoaderを使えば、多くのボイラープレート・コードなしにこれを行うことができる。ClassLoader hibernateClassLoader = AggregateClassLoader.getAggregateClassLoader( portalClassLoader, threadClassLoader); -
新しいクラスローダーを現在のスレッドのコンテキスト・クラスローダーとして設定します。
thread.setContextClassLoader(hibernateClassLoader); -
セッションファクトリを作成し、Hibernateの構成(JNDIデータソースを使用する先に構成されたもの)をロードします。
try { Configuration configuration = new Configuration(); configuration = configuration.configure(); _sessionFactory = configuration.buildSessionFactory(); } catch (Exception e) { _log.error(e, e); }セッションファクトリーサービスは、新しく作成されたクラスローダーを使用して、JNDIデータソースを要求します。
-
スレッドのコンテキストクラスローダとして、元のOSGiクラスローダを復元する。
finally { thread.setContextClassLoader(threadClassLoader); }
HibernateUtil'の他のメソッドは、Hibernate セッションファクトリとそのセッションにアクセスし、動作します。
getSessionFactory()closeSession(Session)openSession()openSession(SessionFactory)
HibernateUtil のようなクラスは、JNDIデータソースなどのJNDIリソースを使用するHibernateセッションの取得を容易にします。 Hibernateを使用するプロジェクトでは、そのバージョンを使用することができます。