MayaResult(WebWork2)

Maya(http://maya.sandbox.seasar.org/)をWebWork2で使うMayaResultを書いてみました(バージョン=webwork2:2.1.7、maya:0.9.9)。MayaServlet使ってServletDispatcherResultとかでも良いんですが、専用のResultを使うほうがWebWork2(XWork)らしいかと。

MayaResult

xwork.xmlでlocationが指定された場合はリクエストパスを偽装してMayaが読み込むテンプレートを指定します。locationが指定されない場合はリクエストURLがそのままMayaに渡ります。

package ww2.examples.maya;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;

import org.seasar.maya.engine.Engine;
import org.seasar.maya.provider.ServiceProvider;
import org.seasar.maya.provider.factory.ProviderFactory;

import com.opensymphony.webwork.ServletActionContext;
import com.opensymphony.webwork.dispatcher.WebWorkResultSupport;
import com.opensymphony.xwork.ActionInvocation;

public class MayaResult extends WebWorkResultSupport {
  protected void doExecute(String location, ActionInvocation actionInvocation)
            throws Exception {
    ServiceProvider provider = ProviderFactory.getServiceProvider();
    if (location == null) {
      provider.initialize(ServletActionContext.getRequest(),
        ServletActionContext.getResponse());
    } else {
      provider.initialize(new MayaResultRequest(ServletActionContext
              .getRequest(), location), ServletActionContext
              .getResponse());
    }
    Engine engine = provider.getEngine();
    engine.doService();
  }

  public static class MayaResultRequest extends HttpServletRequestWrapper {
    private String location;

    public MayaResultRequest(HttpServletRequest request, String location) {
      super(request);
      this.location = location;
    }

    public String getPathInfo() {
      return location;
    }

    public String getServletPath() {
      return "";
    }
  }
}

web.xml

MayaServletのload-on-startupは忘れずに。
Mayaを直で呼びたい場合は*.htmlにMayaServletをマッピングして、ServletDispatcherは別のURLにマッピングする必要があります。*.htmlにマッピングしないとロケーション指定なしMayaResultの恩恵があまり受けられませんが。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app
  PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
  "http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
  <servlet>
    <servlet-name>webwork</servlet-name>
    <servlet-class> 
        com.opensymphony.webwork.dispatcher.ServletDispatcher
    </servlet-class>
    <load-on-startup>1</load-on-startup>
  </servlet>

  <servlet>
    <servlet-name>MayaServlet</servlet-name>
    <servlet-class>org.seasar.maya.impl.MayaServlet</servlet-class>
    <load-on-startup>2</load-on-startup>
  </servlet>

  <servlet-mapping>
    <servlet-name>webwork</servlet-name>
    <url-pattern>*.html</url-pattern>
  </servlet-mapping>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
  </welcome-file-list>
</web-app>

xwork.xml

ロケーション指定ありを呼ぶと、locationパラメータで指定したテンプレート(test.html)が使用され、ロケーション指定なしを呼ぶと呼び出されたURL(ここの場合はtest.html)がテンプレートとして使用されます。

<result-types>
  <result-type class="ww2.examples.maya.MayaResult" name="maya"></result-type>
</result-types>

<!-- ロケーション指定あり -->
<action name="mayaTest"  class="ww2.examples.event.MayaTestAction">
  <result name="success" type="maya">
    <param name="location">test.html</param>
  </result>
</action>
<!-- ロケーション指定なし -->
<action name="test"  class="ww2.examples.event.MayaTestAction">
  <result name="success" type="maya" />
</action>

以下のようにしておけば、successの時はURLで指定しているテンプレートが使用されるので、一々actionごとに書く必要がなくなります。(chainとかstreamとか別のresultを使いたい場合は個々に設定)

<global-results>
    <result name="success" type="maya" />
</global-results>

.htmlをWebWork2に任せられないのであれば、Mayaに渡すURLを「action名+".html"」とかに偽装するResultを作っても良いかも。

WebworkValueStackScriptResolver

ついでにValueStackから値を取得するScriptResolver

package ww2.examples.maya;

import org.seasar.maya.cycle.AttributeScope;
import org.seasar.maya.cycle.script.resolver.ScriptResolver;
import org.seasar.maya.impl.provider.UnsupportedParameterException;

import com.opensymphony.xwork.ActionContext;
import com.opensymphony.xwork.util.OgnlValueStack;

public class WebworkValueStackScriptResolver implements ScriptResolver {
  public Object getVariable(String property) {
    OgnlValueStack valueStack = ActionContext.getContext().getValueStack();

    if (property != null) {
      Object value = valueStack.findValue(property.toString());
      if (value != null) {
        return value;
      }
    }
    return AttributeScope.UNDEFINED;
  }

  public void setParameter(String name, String value) {
    throw new UnsupportedParameterException(name);
  }

  public boolean setVariable(String name, Object value) {
    OgnlValueStack valueStack = ActionContext.getContext().getValueStack();
    valueStack.setValue(name, value);
    return true;
  }
}

maya.provider

mayaのjarからmaya.providerを取ってきて、WEB-INFに置いて、以下の内容をscriptタグ内に追加すればWebworkValueStackScriptResolverが使えるようになります。
(これデフォルトのmaya.providerに設定を追加する方法は無いんでしょうか?)

<resolver class="ww2.examples.maya.WebworkValueStackScriptResolver"/>