2012年07月01日

JSF実装のELライブラリをスタンドアロン・プログラムの中で利用する

Javaによる画面アプリケーションでは、EL式と呼ばれるものがJSPやJSFに組み込まれて利用されます。
そのEL式は、やっぱり基本的にはWebアプリケーションサーバー上で利用することが前提でよく記述されています。一応EL式のライブラリ自体は切りだされていますが、いまいち使い方が分からないです。

例えばスタンドアロンアプリケーションを作り、その中で便利だからELだけ使いたいという場合。
そういう場合でもELのためだけにWebアプリケーションサーバーを導入したり、もしくはそのためにJSFライブラリ全体を持ってきたりするのは面倒です。
ApacheのJEXLApache Commons EL等のライブラリでも良いのですが、ELはGlassFish(mojarra)の実装がやはり一番速度が速いようなので、こちらを何とかして使いたいというかたも多いでしょう。でも、簡単に何もせず、APIだけで組み込むことができなくて苦労します。また調べても、すぐには何も見つかりませんでした(英語でも調べたんですが。。。)

なので、どうやったら簡単に自分のアプリケーションにEL式を組み込めないかなと考えていました。
(考えていただけで、家でのプログラムではEL式を利用するつもりは今のところないのですが)
色々と試してみましたが、スタンドアプリケーション用のELContextとELResolverを実装するしかないのかなぁという結論に至りました。
もっといい方法があるかもしれません。。。

参考までにソースを紹介しますので、ご自由にご利用くださいませ。Javadocコメントは若干適当です。

LocalELContext.java (ELContextと中で利用するELResolver実装です。)

import java.beans.FeatureDescriptor;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import javax.el.ArrayELResolver;
import javax.el.BeanELResolver;
import javax.el.CompositeELResolver;
import javax.el.ELContext;
import javax.el.ELResolver;
import javax.el.FunctionMapper;
import javax.el.ListELResolver;
import javax.el.MapELResolver;
import javax.el.VariableMapper;

import com.sun.el.lang.FunctionMapperImpl;
import com.sun.el.lang.VariableMapperImpl;
import com.sun.faces.el.PropertyResolverChainWrapper;
import com.sun.faces.el.PropertyResolverImpl;

/**
* ローカル変数を起点としてELを実行するためのELコンテキスト
*
* @author Kiruah
*/
public class LocalELContext extends ELContext {

/** ELContext初期化済みかどうかを指定する */
protected static Boolean initialized = Boolean.FALSE;

/** ローカルEL解決者 */
protected static LocalELResolver resolver = null;

/** メソッド呼び出しマッパー */
protected static FunctionMapper functionMapper = null;

/** 変数取得マッパー */
protected static VariableMapper variableMapper = null;

/**
* 初期化処理
*
* @param object 取得対象のオブジェクト
*/
public LocalELContext(
Object object) {

if (initialized == false) {
initialize();
}

if (resolver == null) {
resolver = new LocalELResolver(object);
}
}

/**
* 初期化処理
*/
protected void initialize() {

synchronized (initialized) {

if (initialized == false) {
if (functionMapper == null) {
functionMapper = new FunctionMapperImpl();
}

if (variableMapper == null) {
variableMapper = new VariableMapperImpl();
}

if (LocalELResolver.compositeResolver == null) {
CompositeELResolver elResolver = new CompositeELResolver();

elResolver.add(new ListELResolver());
elResolver.add(new MapELResolver());
elResolver.add(new ArrayELResolver());
elResolver.add(new BeanELResolver());
elResolver.add(new PropertyResolverChainWrapper(new PropertyResolverImpl()));

LocalELResolver.compositeResolver = elResolver;
}

initialized = Boolean.TRUE;
}
}
}

/**
* EL解決者を取得する
*

*

(オーバーライドメソッド)


* @return EL解決者
* @see javax.el.ELContext#getELResolver()
*/
public ELResolver getELResolver() {

return resolver;
}

/**
* メソッド呼び出しマッパーを取得する
*

*

(オーバーライドメソッド)


* @return メソッド呼び出しマッパー
* @see javax.el.ELContext#getFunctionMapper()
*/
public FunctionMapper getFunctionMapper() {

return functionMapper;
}

/**
* 変数取得マッパーを取得する。
*

*

(オーバーライドメソッド)


* @return 変数取得マッパー
* @see javax.el.ELContext#getVariableMapper()
*/
public VariableMapper getVariableMapper() {

return variableMapper;
}

/**
* ローカルEL解決者
*
* @author Kiruah
*/
protected static class LocalELResolver extends ELResolver {

/** ネストされたEL解決者 */
protected static CompositeELResolver compositeResolver = null;

/** 探索対象のオブジェクト */
protected Object object = null;

/**
* コンストラクタ
*
* @param object EL実行対象のオブジェクト
*/
public LocalELResolver(
Object object) {

this.object = object;
}

/**
* 値を取得する
*

*

(オーバーライドメソッド)


* @param context ELコンテキスト
* @param base 探索対象
* @param property EL式
* @return 取得結果
* @see javax.el.ELResolver#getValue(javax.el.ELContext, java.lang.Object, java.lang.Object)
*/
@Override
public Object getValue(
ELContext context,
Object base,
Object property) {

if (base == null) {
return compositeResolver.getValue(
context,
object,
property);
} else {
return compositeResolver.getValue(
context,
base,
property);
}
}

/**
* 型を取得する
*

*

(オーバーライドメソッド)


* @param context ELコンテキスト
* @param base 探索対象
* @param property EL式
* @return 型
* @see javax.el.ELResolver#getType(javax.el.ELContext, java.lang.Object, java.lang.Object)
*/
@Override
public Class getType(
ELContext context,
Object base,
Object property) {

if (base == null) {
return compositeResolver.getType(
context,
object,
property);
} else {
return compositeResolver.getType(
context,
base,
property);
}
}

/**
* 値を設定する
*

*

(オーバーライドメソッド)


* @param context ELコンテキスト
* @param base 探索対象
* @param property EL式
* @param value 設定値
* @see javax.el.ELResolver#setValue(javax.el.ELContext, java.lang.Object, java.lang.Object, java.lang.Object)
*/
@Override
public void setValue(
ELContext context,
Object base,
Object property,
Object value) {

if (base == null) {
compositeResolver.setValue(
context,
object,
property,
value);
} else {
compositeResolver.setValue(
context,
base,
property,
value);
}
}

/**
* 読み取り専用か取得する
*

*

(オーバーライドメソッド)


* @param context ELコンテキスト
* @param base 探索対象
* @param property EL式
* @return 読み取り専用かどうか
* @see javax.el.ELResolver#isReadOnly(javax.el.ELContext, java.lang.Object, java.lang.Object)
*/
@Override
public boolean isReadOnly(
ELContext context,
Object base,
Object property) {

if (base == null) {
return compositeResolver.isReadOnly(
context,
object,
property);
} else {
return compositeResolver.isReadOnly(
context,
base,
property);
}
}

/**
* 特徴の説明情報を取得する
*

*

(オーバーライドメソッド)


* @param context ELコンテキスト
* @param base 探索対象
* @return 特徴の説明情報
* @see javax.el.ELResolver#getFeatureDescriptors(javax.el.ELContext, java.lang.Object)
*/
@Override
public Iterator getFeatureDescriptors(
ELContext context,
Object base) {

if (base == null) {
return compositeResolver.getFeatureDescriptors(
context,
object);
} else {
return compositeResolver.getFeatureDescriptors(
context,
base);
}
}

/**
* 共通のプロパティタイプを取得する
*

*

(オーバーライドメソッド)


*
* @param context ELコンテキスト
* @param base 探索対象
* @return 共通のプロパティタイプ
* @see javax.el.ELResolver#getCommonPropertyType(javax.el.ELContext, java.lang.Object)
*/
@Override
public Class getCommonPropertyType(
ELContext context,
Object base) {

if (base == null) {
return compositeResolver.getCommonPropertyType(
context,
object);
} else {
return compositeResolver.getCommonPropertyType(
context,
base);
}
}
}
}


この中では、ELライブラリで標準で実装されている
・ListELResolver
・MapELResolver
・ArrayELResolver
・BeanELResolver
・PropertResolver
のみCompositeELResolverにセットし包含しています。
ELはトップノードのアクセス時のみgetValueのbaseがnullとなりますので、その場合のみ指定のオブジェクトを探索するようにし、それ以外はbaseを利用するようにするという程度のものです。
初期化処理は冗長です。static {} を使えばいいのですが嫌いなのでこういう感じになっています。

利用する場合は以下の様な実装となります。

Main.java

import java.util.HashMap;
import java.util.Map;

import javax.el.ELContext;
import javax.el.ExpressionFactory;

import com.sun.el.ExpressionFactoryImpl;
import com.sun.el.ValueExpressionImpl;

/**
* EL組み込みのサンプル
*
* @author Kiruah
*/
public class Main {

/**
* メイン
*
* @param args コマンドライン引数
*/
public static void main(
String[] args) {

Map container = new HashMap();
container.put(
"hoge1",
"hogehoge1");
container.put(
"hoge2",
"hogehoge2");

String el = "${hoge1}";
Object value = get(
container,
el);

System.out.println(value);
}

/**
* EL式を利用して取得対象から値を取得します。
*
* @param target 取得対象
* @param el EL式
* @return 取得結果
*/
public static Object get(
Object target,
String el) {

ELContext context = new LocalELContext(target);
ExpressionFactory ef = ExpressionFactoryImpl.newInstance();
ValueExpression ve = (ValueExpression) ef.createValueExpression(
context,
el,
Object.class);

Object value = ve.getValue(context);

return value;
}
}


こうすることで、自前で用意しているコンテナがあれば、そこからEL式を利用して値を取得することができます。

ちょっと実装しなければならないとできなかったのが、自分の調査・スキル不足ならば、もっと勉強や自己研鑚が必要です。
頑張らねば。
posted by Kiruahさん at 01:00| Comment(0) | TrackBack(0) | ノウハウ
この記事へのコメント
コメントを書く
お名前: [必須入力]

メールアドレス: [必須入力]

ホームページアドレス:

コメント: [必須入力]

認証コード: [必須入力]


※画像の中の文字を半角で入力してください。
※ブログオーナーが承認したコメントのみ表示されます。
この記事へのトラックバックURL
http://blog.sakura.ne.jp/tb/56792402
※ブログオーナーが承認したトラックバックのみ表示されます。
※言及リンクのないトラックバックは受信されません。

この記事へのトラックバック