Struts1.2 逆引き辞典 戻る
目次 目的、対象

Struts 1.2 を利用するにあたり「こんな使い方あるよ」な「逆引き辞典」的な記録方法になります。

JSPタグ関連
SELECTタグを出力

「性別:男性」といったSELECTタグを実装する方法です。

一般的にStrutsではFORMに具体的な値(男性を表す"1"など)が設定されています。これをJSPに表示する際に「男性」と表示するには以下を参考にして下さい。

Java public class HogeServlet extends HttpServlet { public class Personal { private String id; private String gender; // Getter,Setterは省略 } public void doGet(HttpServletRequest request, HttpServletResponse response) { Personal dto = new Personal(); dto.setGender("1"); // genderの先頭が大文字なのは注意 request.setAttribute("DTO", dto); Map map = new TreeMap(); map.put("1", "男性"); map.put("2", "女性"); request.setAttribute("genderMap", map); request.getRequestDispatcher("hoge.jsp").forward(request, response); } }
hoge.jsp <html:select name="DTO" property="gender"> <html:option value="">選択して下さい</html:option> <!-- この行はなくてもよい --> <html:optionsCollection name="genderMap" value="key" label="value" /> </html:select>
SELECTタグで出力するようなものを「input type=radio」で出力

上記のSELECTタグをradioにて出力する実装する方法です。

hoge.jsp(radioの場合) <logic:iterate id="row" name="genderMap"> <html:radio idName="row" name="DTO" property="gender" value="key" /> <bean:write name="row" property="value"/> </logic:iterate>
SELECTタグで出力するようなものを「テキスト」で出力

上記のSELECTタグをプレーンテキストにて出力する実装する方法です。

hoge.jsp(テキストの場合) <c:out value="${genderMap[DTO.gender]}" />
hoge.jsp(テキストでc:outが使えない場合) <bean:define id="genderValue" name="DTO" property="gender" type="java.lang.String" /> <bean:write name="genderMap" property="<%=genderValue%>" />
StrutsConfig関連
actionタグのparameter属性

Strutsを利用する方で知名度が低いのがactionタグのparameter属性かと思います。まずは使い方です。

StrutsConfigの例 <struts-config> <action-mappings type="org.apache.struts.action.ActionMapping"> <action path="/likePerson" type="com.hogehoge.LikeAction" name="Person" scope="session" validate="false" parameter="LikePerson"> <forward name="success" path="/WEB-INF/jsp/likedPerson.jsp" redirect="false"> <forward name="notfound" path="/WEB-INF/jsp/likedPerson.jsp" redirect="false"> </action> </action-mappings> </struts-config>

path="/likePerson"にて呼び出される設定が記述してあります。ここでparameter属性には"LikePerson"が設定されています。これをJavaで利用します。

Javaで利用する package com.hogehoge; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.struts.action.Action; import org.apache.struts.action.ActionForm; import org.apache.struts.action.ActionForward; import org.apache.struts.action.ActionMapping; public class LikeAction extends Action { public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { System.out.println(mapping.getParameter()); return mapping.findForward("success"); } }

Javaで利用するにはActionMapping#getParameter()を呼び出すのみです。戻り値はStringですのでStrutsConfigに記述してあるparameter属性の値(今回でいうとparameter="LikePerson"なので"LikePerson")が取得できます。

単に特定の文字列を渡すだけの機能ですが(ForwardActionはその典型でしょう)、このパラメータにロジッククラスやサービスクラスといったクラスのFQCNを記述することで、Actionからインスタンス生成などに利用することができます。

これ単体では目立った機能ではないのですがワイルドカードマッピングと組み合わせるととても強力です。

set-propretyタグ

Validator機能を利用する際、こちらのようなソースをみたことがあると思います。このset-propertyタグは非常に考えられた優秀なタグなので紹介します。

set-propertyタグは単純に値をセッタするためのタグになります。つまりValidatorプラグインではValidatorPlugInをインスタンス化したのち、pathnamesという名のプロパティに"/validator-rules.xml,/validation.xml"という値を設定しているというだけなのです。

このset-propertyタグが「考慮された優秀な」というのは、非常に多くの親要素の子要素として記述することができるからです。

Validatorプラグインを使う <plug-in className="org.apache.struts.validator.ValidatorPlugIn"> <set-property property="pathnames" value="/validator-rules.xml,/validation.xml"> </plug-in>

こちらのStrutsConfigを参照下さい。こちらにはactionのpathに/blankが指定された例になります。こちらではactionタグに利用するクラスがActionMappingと指定されています。このクラスを拡張してみます。

set-property対応前 <struts-config> <action-mappings type="org.apache.struts.action.ActionMapping"> <action path="/blank" type="org.apache.struts.actions.ForwardAction" parameter="/WEB-INF/jsp/blank.jsp"> </action> </action-mappings> </struts-config>

こちらが拡張したクラスになります。拡張といってもプロパティを増やした程度のクラスです。

ActionMappingEx.java public class ActionMappingEx extends ActionMapping { private String parameter1; // getter,setter等、説明に不要な記述は省略 }

こちらのparameter1に値を設定してみます。

set-property対応後 <struts-config> <action-mappings type="org.apache.struts.action.ActionMappingEx"> <action path="/blank" type="org.apache.struts.actions.ForwardAction" parameter="/WEB-INF/jsp/blank.jsp"> <set-property property="parameter1" value="hogehoge"> </action> </action-mappings> </struts-config>

このようにdtdファイルを拡張しなくても、プロパティを増やすことができる仕組みになっています。優秀な作りだと思います。

またこのタグはdtdファイルによると、data-source,form-bean,form-property,exception,forward,action,controller,message-resources,plug-inの子要素として記述することができます。

ワイルドカードマッピング

ServletではURLのマッピングに先頭もしくは末尾に*(アスタリスク)を用いることができます。ですが若干不便です。このマッピングをカバーする機能としてワイルドカードマッピングという機能があります(上位互換と思って下さい)。

現在、下記StrutsConfigにはPersonという名の名詞(データの塊の意)とblank, condition, likeという動詞(名詞に対する動作)から構成されているようです。

変更前 <struts-config> <form-beans type="org.apache.struts.action.ActionFormBean"> <form-bean name="Person" type="com.hogehoge.PersonForm"/> </form-beans> <action-mappings type="org.apache.struts.action.ActionMapping"> <action path="/blankPerson" type="org.apache.struts.actions.ForwardAction" parameter="/WEB-INF/jsp/blankPerson.jsp"> <action path="/conditionPerson" type="org.apache.struts.actions.ForwardAction" parameter="/WEB-INF/jsp/conditionPerson.jsp" name="Person" validate="false"> <action path="/likePerson" type="com.hogehoge.LikeAction" name="Person" scope="session" validate="false" parameter="LikePerson"> <forward name="success" path="/WEB-INF/jsp/likedPerson.jsp" redirect="false"> <forward name="notfound" path="/WEB-INF/jsp/likedPerson.jsp" redirect="false"> </action> </action-mappings> </struts-config>

今回は上記のStrutsConfigをワイルドカードマッピングに対応してみます。

まずはactionタグのpath属性の中で、名詞であるPersonを*(アスタリスク)に置き換えます。actionタグの他の属性のPersonは{1}に置き換えます。これにて終了です。

変更後 <struts-config> <form-beans type="org.apache.struts.action.ActionFormBean"> <form-bean name="Person" type="com.hogehoge.PersonForm"/> </form-beans> <action-mappings type="org.apache.struts.action.ActionMapping"> <action path="/blank*" type="org.apache.struts.actions.ForwardAction" parameter="/WEB-INF/jsp/blank{1}.jsp"> <action path="/condition*" type="org.apache.struts.actions.ForwardAction" parameter="/WEB-INF/jsp/condition{1}.jsp" name="{1}" validate="false"> <action path="/like*" type="com.hogehoge.LikeAction" name="{1}" scope="session" validate="false" parameter="Like{1}"> <forward name="success" path="/WEB-INF/jsp/liked{1}.jsp" redirect="false"> <forward name="notfound" path="/WEB-INF/jsp/liked{1}.jsp" redirect="false"> </action> </action-mappings> </struts-config>

この作業を行うことによって、いままでは「path="/blankPerson"」のみに対応していましたが「path="/blankBook"」「path="/blankCompany"」にも対応しました。

ただし当然ですが「name="Book"」であるActionフォームが存在しません。前記の対応を行わないと動作させられませんが、StrutsConfigファイルの記述量は相当量削減したと思います。

その他
複数のFormFile

同じ名前でFormFileを複数Submitするサンプルになります。

Submitするjsp <html:form action="それぞれ" enctype="multipart/form-data"> <html:file property="file[0]" /><br /> <html:file property="file[1]" /><br /> <html:file property="file[2]" /><br /> <html:submit /> </html:form>
値をうけとるActionForm import java.util.HashMap; import java.util.Map; import org.apache.struts.upload.FormFile; import org.apache.struts.validator.ActionForm; @SuppressWarnings("serial") public class MultipleFormFileForm extends ActionForm { private Map<Integer, FormFile> map = new HashMap<>(); public FormFile getFile(int index) { return map.get(index); } public void setFile(int index, FormFile file) { map.put(index, file); } public FormFile[] getFiles() { return map.values().toArray(new FormFile[map.size()]); } }
BeanUtilsの脆弱性対策

BeanUtilsの脆弱性として「class.classLoader.xxx」のように指定するとgetClass().getClassLoader().setXxxx(???)のように動作してしまうものがあります。

こちらの脆弱性を対応するために以下を利用します。

対応するためのリスナー package jp.co.mclnet.servlet30; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import javax.servlet.annotation.WebListener; import org.apache.commons.beanutils.PropertyUtils; import org.apache.commons.beanutils.SuppressPropertiesBeanIntrospector; @WebListener public class SuppressPropertiesListener implements ServletContextListener { public void contextDestroyed(ServletContextEvent event) { } public void contextInitialized(ServletContextEvent event) { PropertyUtils.addBeanIntrospector( SuppressPropertiesBeanIntrospector.SUPPRESS_CLASS); PropertyUtils.clearDescriptors(); } }

「SuppressPropertiesBeanIntrospector.SUPPRESS_CLASS」定数を参照すると以下のようなことも対応できそうです。

対応メソッドを増やす例 String[] properties = { "test", "other", "oneMore" }; SuppressPropertiesBeanIntrospector introspector = new SuppressPropertiesBeanIntrospector(Arrays.asList(properties));

また、こちらのクラスは「@WebListener」アノテーションを利用していますのでweb.xmlに登録は不要ですが、具体的に記述する場合は以下を参考にして下さい。

web.xmlに追記する例 <listener> <listener-class>jp.co.mclnet.servlet30.SuppressPropertiesListener</listener-class> </listener>