-式言語エンジン

式言語エンジンの持ち方を考えていたところ、式言語エンジンへの疎結合だけでなく、背後のドメインモデルとのバインディングのやり方のカスタマイズも解決できたと思います。仮にインターフェイスだけ組んでみました。

package org.seasar.maya;
import javax.servlet.jsp.el.VariableResolver;
/**
 * Mayaアプリケーション中に記述された式言語を評価する式言語エンジンへのアクセスを
 * 抽象化したインターフェイス。OGNL(デフォルト)やELのエンジンをラップする。
 * 
 * @author Masataka Kurihara (Gluegent, Inc.)
 */
public interface PoolableVariableResolver extends VariableResolver {
  /**
   * このリゾルバをプールへ戻す際にMayaエンジン側から呼び出される
   * 初期化メソッド
   */
  void release();
  /**
   * 式言語中で参照される、組み込み変数の追加
   * @param name  変数名
   * @param variable  組み込み変数
   */
  void addVariable(String name, Object variable);
  /**
   * 式評価のルートとなるオブジェクトの設定
   * @param root  ルートオブジェクト
   */  
  void setRoot(Object root);
}

このインターフェイスを実装したOGNLエンジンのラッパーを作ります。Mayaの中で取り扱うのは値の取得だけで、JSFで行われるようなユーザー入力値のドメインモデルへの反映は無いので、シンプルにしてます。ELのAPIは見切れてないのですが使われ方が同じですから大丈夫だと思います。私の知ってる世界では、RhinoやGroovyなどならこのインターフェイスのメソッド実装だけで差し替えクラスを作れると判断しています。
ドメインモデルへの接続は、OGNLならPropertyAccessorで拡張すれば良いと考えています。PropertyAccessorの例として、Seasar2のSingletonS2Containerにアクセスするものや、Springにアクセスするものを作ればよいでしょう。ExpressionEngineの実装クラスは、式言語エンジンの薄いラッパーであって、アダプタの実装者は当然採用する式言語エンジンのAPIを熟知している必要があります。これはMaya利用者ではなく、Maya開発者に強いることなので特別なにかの手当てはしません。この式言語エンジンの初期化(生成・アダプタの登録)は、どこかに置くオーバーライド可能なファクトリーメソッドで行うようにします。

/**
 * 式言語エンジンラッパーの生成
 * @return	式言語エンジンラッパー
 */
PoolableVariableResolver createVariableResolver();

/**
 * 式言語エンジンの初期化を行う
 * @param resolver プールから取り出された式言語エンジン
 */
void initValiableResolver(PoolableVariableResolver resolver);

主要DIコンテナへの対応作業は、OGNLのPropertyAccessorの対応開発に集約します。OGNL3でのVarialeResolverのサンプル実装は以下のとおりです。

package org.seasar.maya.runtime.resolver.ognl;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import javax.servlet.jsp.el.ELException;
import org.ognl.el.Expression;
import org.ognl.el.OgnlException;
import org.ognl.el.PropertyAccessor;
import org.ognl.el.extensions.DefaultExecutionEnvironment;
import org.seasar.maya.PoolableVariableResolver;
/**
 * OGNL3を利用したリゾルバ実装クラス
 * 
 * @author Masataka Kurihara (Gluegent, Inc.)
 */
public class OgnlVariableResolver implements PoolableVariableResolver {
  private Object root;
  private Set variableNames = new HashSet();
  protected DefaultExecutionEnvironment environment;
  public OgnlVariableResolver() {
    environment = new DefaultExecutionEnvironment();
  }
  public void setPropertyAccessor(Class clazz, PropertyAccessor accessor) {
    environment.setPropertyAccessor(clazz, accessor);
  }
  public void release() {
    root = null;
    synchronized(variableNames) {
      for(Iterator it = variableNames.iterator(); it.hasNext();) {
        environment.removeVariableValue( (String)it.next() );
      }
      variableNames.clear();
    }
  }
  public void addVariable(String name, Object variable) {
    variableNames.add(name);
    environment.setVariableValue(name, variable);
  }
  public void setRoot(Object root) {
    this.root = root;
  }
  public Object resolveVariable(String el) throws ELException {
    try {
        Expression expression = environment.parseExpression(el);
      return environment.getValue(expression, root);
    } catch(OgnlException e) {
      throw new ELException(e);
    }
  }
}

Kijimunaからコピペしただけです。で、よく考えたら組み込みモデルは、OGNLのVariableじゃないんだったな。OGNLのVariableは頭に「#」をつけてアクセスします。ということは、Rootオブジェクトとプロパティアクセッサで細工しないといかんのです。