SpringでS2Daoを使う -その3-

 かなり間が空いてしまいましたが、SpringでS2Daoを使うの3回目です。S2Daoで実装クラス(Employee2DaoImpl)にS2DaoInterceptorをかけた場合のエラーとS2のバージョンを2.4.xにしたものを下記におきました。
 http://www.asahi-net.or.jp/~wh6n-icmr/spring-s2dao-example2.zip

 変更のポイントは、下記のようにinterceptorのかけ方を変更したのとS2の2.4.xへの対応(JTAがらみ?のインタフェース変更)のために、dao.xmlを変更したことです。

Class targetClass = ClassUtil.forName(className);
Class enhancedClass;

AspectWeaver weaver = new AspectWeaver(targetClass, null);

Method methods = targetClass.getMethods();
for (int i = 0; i < methods.length; ++i) {
	Method method = methods[i];
	if (isBridgeMethod(method)) {
		continue;
	}

	List interceptorList = new ArrayList();
	for (int j = 0; j < interceptorNames.length; j++) {
		MethodInterceptor interceptor = (MethodInterceptor) getBeanFactory()
				.getBean(interceptorNames[j]);
		interceptorList.add(interceptor);
	}
	if (!isApplicableAspect(method)) {
		continue;
	}
	if (interceptorList.size() == 0) {
		weaver.setInterceptors(method, new MethodInterceptor[0]);
	} else {
		weaver.setInterceptors(method,
			(MethodInterceptor) interceptorList
					.toArray(new MethodInterceptor[interceptorList.size()]));
	}
}
enhancedClass = weaver.generateClass();

 制限としては、FileSystemBeanAutoRegister.getRootDirの実装がS2と異なり手抜きをしているため、beanRefContext.xmlが存在することが前提となっています。これは、ちょっといけてないので本当は修正したかったのですが、S2のようにルートパスを取得する方法がわからなかったため、そのままになっています。あとは、各ライブラリが若干古いのですが、ちょっと整理する時間がなかったので、そのままあげています。その他、詳細はダウンロード後readme.txtを確認して下さい。

SpringでS2Daoを使う -その1-

 久しぶりの書き込みになってしまいましたが、ボチボチ書き込みして行こうと思います。
 最近Springを使う機会があったのですが、S2との違いに戸惑うばかり・・・。設定ファイルを書くのも面倒だし、Dao層にもS2Daoのように簡単に使えるものがないし。というわけで、SpringでBeanの自動登録とS2Daoが利用できないかと思い試してみました。こんなことをやる人はいないと思いますが、参考までにプロジェクト毎下記においておきます。一応、 S2Daoのs2-dao-examplesが基本動作することを確認しています。(一部を除く)
 http://www.asahi-net.or.jp/~wh6n-icmr/spring-s2dao-example.zip
 http://www.asahi-net.or.jp/~wh6n-icmr/spring-s2dao-example_src.zip (srcのみ)

 具体的に作成(修正)したプログラムは、下記となります。

  • S2Dao関連
    • SpringValueTypeFactoryImpl.java
  • Bean登録関連
    • AbstractAutoRegister.java
    • AbstractBeanAutoRegister.java
    • FileSystemBeanAutoRegister.java
      • S2本体のAutoRegisterのソースをベースに、SpringでBeanの自動登録をサポートするクラスを作成(インタフェースのインスタンス化も行います。)
      • BeanFactoryPostProcessorの実装クラスとなっているため、Bean登録時に実行されます。

Daoの登録は、beans.xml内の下記の設定で行っています。

<bean class="framework.autoregister.FileSystemBeanAutoRegister">
    <property name="addPackageName">
        <value>examples.dao</value>
    </property>
    <property name="addClassNames">
        <value>.*Dao,.*Manager</value>
    </property>
</bean>
<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
    <property name="beanNames">
        <value>*Dao,*Manager</value>
    </property>
    <property name="interceptorNames">
        <list>
            <value>s2DaoInterceptor</value>
            <value>traceInterceptor</value>
        </list>
    </property>
</bean>

 このようにS2同様のイメージでBeanの自動登録が可能です。

【制限事項】
 (1)Employee2DaoClientTestが動作しません。(原因不明)
 (2)S2DaoTestCaseは利用できません。
 (3)S2Dao Tiger/S2Dao Backport175の動作確認はしていません。

 Spring初心者だったので、id:koichikさんのSpring Framework 入門記には大変お世話になりました。ありがとうございました。

SpringでS2Daoを使う -その2-

 動かないのは、S2Daoで実装クラス(Employee2DaoImpl)にS2DaoInterceptorをかけた場合です。getEmployeesが見つからないとMethodNotFoundRuntimeExceptionが発生してしまっています。AbstractBeanAutoRegister.registerで下記のコードでクラス生成後、Springに登録しているのですがこの辺が駄目なようですが、色々試してみたもののどうしたら良いかわかりませんでした。

Class targetClass = ClassUtil.forName(className);
AspectWeaver weaver = new AspectWeaver(targetClass, null);
Method[] methods = targetClass.getDeclaredMethods();
for (int i = 0; i < methods.length; ++i) {
    weaver.setInterceptors(methods[i], new MethodInterceptor[0]);
}
Class enhancedClass = weaver.generateClass();	

 あとは、S2のバージョンを2.4.xにしたら(2.3.xではOK)、JTA関連の実装が変わったのかS2のTransactionManagerImplをSpringのJtaTransactionManagerのpropertyに設定するとTypeMismatchExceptionが発生しました。

JSTLのi18nタグ(fmt)を利用したら文字化け(回避方法)

 id:n-ichimura:20060302のTomcat4.1(Servlet 2.3)環境でJSTLi18nタグのを利用した場合に文字化けする現象の回避方法を書いていなかったので、参考までに書いておきます。あまり良い回避方法とも思えませんが、私はJSTLのソースを書き換えてしまいました。
 修正対象は、org.apache.taglibs.standard.tag.common.fmt.SetLocaleSupportです。setResponseLocaleメソッドのresponse.setLocale(locale);をコメントアウトすると、文字エンコーディングが暗黙でServletResponseに設定されなくなります。

S2Strutsにおけるチェックボックスの利用について

 S2StrutsPOJO Formでチェックボックスを利用しています。POJO Formのスコープがセッションの場合、JSPと記述していると、チェックがついた状態(check="on")からチェックを外しても、チェックを外したプロパティのSetterは呼び出されないので、セッション内の"on"が残ってしまい、呼び出されたActionクラスでは状態の判断ができません。
 Javascriptで、別のプロパティにセットするなりすれば対応可能だと思いますが、スマートな解決策はあるのでしょうか?reset()メソッドが記述できれば良いのですが。

DWRを使う

 下記のURLの内容を参考にDWRを使ってみました。
  http://www3.vis.ne.jp/~asaki/p_diary/diary.cgi?Date=2005-11-04
 このcreatorを利用したところ、引数で全てのパラメータを渡すのであれば問題なかったのですが、セッションに格納されているログインユーザの情報を利用したかったので、下記のように書き換えました。

S2Creator.java

package sample.dwr;

import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.seasar.framework.beans.BeanDesc;
import org.seasar.framework.beans.PropertyDesc;
import org.seasar.framework.beans.factory.BeanDescFactory;
import org.seasar.framework.container.ComponentDef;
import org.seasar.framework.container.S2Container;
import org.seasar.framework.container.factory.SingletonS2ContainerFactory;
import org.seasar.struts.util.RequestUtil;
import uk.ltd.getahead.dwr.WebContextFactory;
import uk.ltd.getahead.dwr.create.AbstractCreator;

public class S2Creator extends AbstractCreator {

    private String beanName;
    private Class clazz;

    public void setBeanName(String beanName) {
        this.beanName = beanName;
    }

    public Class getType() {
        if (clazz == null) {
            try {
                S2Container container = SingletonS2ContainerFactory.getContainer();
                HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();
                container.setRequest(request);
                ComponentDef def = container.getComponentDef(beanName);
                clazz = def.getComponentClass();
            } catch (Exception ex) {
                clazz = Object.class;
            }
        }
        return clazz;
    }

    public Object getInstance() throws InstantiationException {

        S2Container container = SingletonS2ContainerFactory.getContainer();
        ComponentDef cd = container.getComponentDef(beanName);
        Object component = cd.getComponent();
        BeanDesc beanDesc = BeanDescFactory.getBeanDesc(cd.getComponentClass());

        importProperties(component, container, beanDesc);
        return container.getComponent(beanName);
    }

    private void importProperties(Object component, S2Container container, BeanDesc beanDesc) {
        for (int i = 0; i < beanDesc.getPropertyDescSize(); i++) {
            PropertyDesc propertyDesc = beanDesc.getPropertyDesc(i);
            importProperty(component, container, propertyDesc);
        }
    }

    private static Map primitiveMap = new HashMap();

    static {
        primitiveMap.put(Character.TYPE, Character.class);
        primitiveMap.put(Short.TYPE, Short.class);
        primitiveMap.put(Integer.TYPE, Integer.class);
        primitiveMap.put(Long.TYPE, Long.class);
        primitiveMap.put(Double.TYPE, Double.class);
        primitiveMap.put(Float.TYPE, Float.class);
        primitiveMap.put(Boolean.TYPE, Boolean.class);
    }

    private Class getPrimitiveWrappedClass(Class primitiveClass) {
        return (Class) primitiveMap.get(primitiveClass);
    }

    private void importProperty(Object action, S2Container container, PropertyDesc propertyDesc) {
        if (!propertyDesc.hasWriteMethod()) {
            return;
        }

        String propertyName = propertyDesc.getPropertyName();
        Object value = getValue(container, propertyName);
        if (value == null) {
            return;
        }

        Class propertyType = propertyDesc.getPropertyType();
        if (propertyType.isPrimitive()) {
            propertyType = getPrimitiveWrappedClass(propertyType);
        }
        if (propertyType.isInstance(value)) {
            propertyDesc.setValue(action, value);
        }
    }

    private Object getValue(S2Container container, String name) {
        Object var = RequestUtil.getValue(container.getRequest(), name);
        if (var != null) {
            return var;
        }

        if (container.hasComponentDef(name)) {
            return container.getComponent(name);
        }
        return null;
    }
}

 importPropertiesの部分は、HttpServletRequestとSessionのコンポーネントをDIするために、S2StrutsのBindingUtilを参考に追加しています。
 このcreatorをWEB-INF直下のdwr.xmlに登録します。

dwr.xml

<!DOCTYPE dwr PUBLIC
    "-//GetAhead Limited//DTD Direct Web Remoting 1.0//EN"
    "http://www.getahead.ltd.uk/dwr/dwr10.dtd">
<dwr>
  <init>
    <creator id="s2" class="sample.dwr.S2Creator"/>
  </init>
  <allow>
    <convert converter="bean" match="sample.dto.Sample1Dto"/>
    <create creator="s2" javascript="Sample1" scope="request">
      <param name="beanName" value="sample1Action"/>
      <include method="findSample1"/>
    </create>
    <convert converter="bean" match="sample.dto.Sample2Dto"/>
    <create creator="s2" javascript="Sample2" scope="request">
      <param name="beanName" value="sample2Action"/>
      <include method="findSample2List"/>
    </create>
  </allow>
</dwr>

 Sample1ActionとSample2Actionは、S2にコンポーネントを登録しておきます。

Sample1Action.java

package sample.action.dwr;
import sample.dto.Sample1Dto;

public interface Sample1Action {
    public Sample1Dto findSample1(String cd);
}

Sample2Action.java

package sample.action.dwr;
import java.util.List;

public interface Sample2Action {
    public List findSample2List(String cd);
}

 このコンポーネントを呼び出すJSPは、下記のようになります。

sample.jsp

<script type="text/javascript" src="../dwr/engine.js"></script>
<script type="text/javascript" src="../dwr/util.js"></script>
<script type="text/javascript" src="../dwr/interface/Sample1.js"></script>
<script type="text/javascript" src="../dwr/interface/Sample2.js"></script>
<script type="text/javascript">
<!--
function updateSample1(cd) {
    Sample1.findSample1(getSample1, cd);
}
    
function getSample1(sample1) {
    DWRUtil.setValue("cd", sample1.cd);
    DWRUtil.setValue("name", sample1.name);
}

function updateSample2(cd) {
    Sample2.findSample2List(getSample2List, cd);
}
    
function getSample2List(sample2List) {
    DWRUtil.removeAllOptions("sample2");
    DWRUtil.addOptions("sample2", sample2List, "cd", "name");
}
//-->
</script>
<input type="text" name="cd" value="">
<input type="text" name="name" value="">
<input type="button" onclick="updateSample1();">

<select name="sample2" id="sample2" onchange="updateSample2(this.value);">
   <option value="01">テスト01</option>
   <option value="02">テスト02</option>
   <option value="03">テスト03</option>
   <option value="04">テスト04</option>
</select>

S2PagerとBLOB

 S2Pagerを使っているのですが、ファイルアップロードされたファイルをS2DaoOracleのBLOB型に格納しようとしたところ、下記のエラーが発生しました。

エラー内容

org.seasar.framework.exception.SQLRuntimeException:[ESSR0071]SQLで例外が発生しました。
理由はjava.sql.SQLException: ストリームをScrollableResultSetまたはUpdatableResultSetにバインドできません
 at org.seasar.extension.jdbc.impl.BasicHandler.bindArgs(BasicHandler.java:110)
 at org.seasar.dao.impl.AbstractAutoHandler.execute(AbstractAutoHandler.java:142)
 at org.seasar.dao.impl.AbstractAutoHandler.execute(AbstractAutoHandler.java:122)
 at org.seasar.dao.impl.AbstractAutoStaticCommand.execute(AbstractAutoStaticCommand.java:49)
 at org.seasar.dao.interceptors.S2DaoInterceptor.invoke(S2DaoInterceptor.java:51)
 at org.seasar.dao.pager.PagerS2DaoInterceptorWrapper.invoke(PagerS2DaoInterceptorWrapper.java:63)
 at org.seasar.framework.aop.impl.NestedMethodInvocation.proceed(NestedMethodInvocation.java:41)
 at org.seasar.framework.aop.interceptors.TraceInterceptor.invoke(TraceInterceptor.java:50)
 at org.seasar.framework.aop.impl.NestedMethodInvocation.proceed(NestedMethodInvocation.java:41)
 at org.seasar.framework.aop.interceptors.InterceptorChain.invoke(InterceptorChain.java:41)

 ScrollableResultSetを利用した場合、byte配列からBLOGからのINSERTでエラーになるようです。とりあえず、エラーを回避するため、dao.diconの設定をfalseとしましたが、これでも駄目でした。されに、下記の変更を加えたところ、一応ページングも可能でBLOB型へのINSERTも可能な状況になりました。


          ↓

 BLOB型が含まれる時のみ、スクロールカーソルを利用するようにはできないものか調べてみましたがわからず・・・。時間ができたら調べてみよう。