Struts Catalog
Toward an overview of Struts Design Patterns and Strategies.
Sturtsデザインパターンと戦略の概要についてSince this is catalog, not every application is expected to implement every pattern or strategy.
これはカタログのため、全てのアプリケーションが、全てのパターンまたは戦略をインプリメントすることを想定しているわけではありません。
Remove the sample "admin" mappings from a production application
サンプルの“admin”マッピングを作成するアプリケーションから削除してください
The example struts-config.xml file includes the following mappings for administration purposes:
例のstruts-config.xmlファイルは、管理目的のために以下のマッピングを含みます:
<!-- The standard administrative actions available with Struts -->
<!-- These would be either omitted or protected by security -->
<!-- in a real application deployment -->
<action path="/admin/reload"
type="org.apache.struts.actions.ReloadAction"/>and so forth.
など
If these are not removed when the application is deployed, malicious users can create havoc by removing mappings. They might notice, for example, that they are sent to/login.doto log in. They can remove this mapping by sending a request to/admin/removeMapping.do?path=/login, effectively removing the front door of the site and preventing all users from entering.
もし、アプリケーションをデプロイする時にこれらを削除しない場合、悪意のあるユーザがマッピングの削除によって破壊する事ができます。 かれらは、たとえば、ログインは/login.doに送られている事に気づくかもしれません。 かれらは、/admin/removeMapping.do?path=/loginにリクエストを送ることで、 このマッピングが削除できてしまい、 サイトのフロントドアを効果的に削除し全てのユーザの参加を、妨害する効果があります。
It is important to follow the advice in the sample configurations, and either omit these mappings from a shipping application, or protect them with security.
サンプルコンフィグレーションのアドバイスに従う事と、 出荷するアプリケーションからこれらのマッピングを省略すること、 またはセキュリティによってそれらを保護することは重要です。
Link only to Actions
Link only to Action(Actionだけにリンクする)In a Model 2 environment, the pages are suppose to be pretty but stupid. Flow to a page should go through an
Action first, and the Action should assemble all the data that a page might need and put it into the request or session context. The page then just grabs what it needs, and figures out how to display it. The Action
may not known the address of the page (that's in the config file), but it does need a punch list of what data the page may require. Every page should have an Action handler. One Action may handle several different
pages, or a page could be handled by several different Actions (e.g. an error page), but each page should have at least one handler.
モデル2環境内では、ページは立派だが単純であると想定されます。 ページへのフローは、Actionを最初に通過するべきです。 そして、Actionはページが必要とするかもしれないデータを全て集め、 リクエストまたはセッションコンテキストにそれをプットすべきです。 その後、ページは必要とするものを得、そしてそれを表示する方法を計算します。 アクションはページのアドレスを知らない(それは、コンフィグファイルにはいっている)、 しかし、ページがどんなデータを要求するかのパンチリストは必要です。 全てのページはアクションハンドラをもつべきです。 1つのActionはいくつかの異なるページを扱うかもしれません、また、 幾つかの異なるアクションによってページを扱うかもしれません(エラーページのように)。 しかし、それぞれのページは最低でも1つのハンドラを持つべきです。If a JSP ends up on your browser's address bar after the initial index page, then you're missing an Action ;-)
もし、最初のインデックスページよりあとで、 JSPがあなたのブラウザのアドレスバーの最後になるなら、 あなたはActionについてミスをしています:-)[Since .04]
Use a forwarding Action for static pages
静的なページには、forwarding Actionを使ってくださいIf your page does not need any data collected for it, you can use a generic forwarding Action as a handler. This way if you're needs change, you can just update the configuration file, and you have not given away the location of your pages.
もしあなたのページに、収集したデータを少しも必要としないなら、 ハンドラとして generic forwarding Action を使う事ができます。 この方法では、(フォワード先のページのURLが) 変更になったとしても単にコンフィギュレーションファイルを修正するだけで 良く、自分のページ内のリンクを修正する必要はありません。/** * Generic dispatcher to ActionForwards. * Action Forwardに対するGeneric dispatcher * * @author Ted Husted * @version $Revision: 1.1 $ $Date: 2001/08/01 $ */ public final class DispatchForward extends Action { // --------------------------------------------------------- Public Methods /** * Forward request to "cancel", {forward}, or "error" mapping, where {forward} * is an action path given in the parameter mapping or in the request as * "forward=actionPath". * "cancel",{forward},又は"error"マッピングに対する Forward requestで、 * {forward}は“forward=actionPath”としてパラメータマッピングまたは、 * リクエストの中で与えられたアクションパスです。 * * * @param mapping The ActionMapping used to select this instance * @param actionForm The optional ActionForm bean for this request (if any) * @param request The HTTP request we are processing * @param response The HTTP response we are creating * * @exception IOException if an input/output error occurs * @exception ServletException if a servlet exception occurs */ public ActionForward perform(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { // -- isCancelled? if (isCancelled(request)) { form.reset(mapping,request); return (mapping.findForward("cancel")); } // -- Locals ActionForward thisForward = null; String wantForward = null; // -- Check internal parameter for forward wantForward = mapping.getParameter(); // -- If not found, check request for forward if (wantForward==null) wantForward = request.getParameter("forward"); // -- If found, consult mappings if (wantForward!=null) thisForward = mapping.findForward(wantForward); // -- If anything not found, dispatch error if (thisForward==null) { thisForward = mapping.findForward("error"); ActionErrors errors = new ActionErrors(); errors.add(ActionErrors.GLOBAL_ERROR, new ActionError("action.missing.parameter")); saveErrors(request, errors); } return thisForward; } // end perform } // end Action[Since .04]
Place all Java ServerPages below WEB-INF
全てのJava ServerPagesをWEB-INFの下に置きますNote: This is not supported by all containers (e.g. WebLogic). If you believe it should be, contact your vendor. The Servlet 2.3 specification makes it much clearer that this is allowed.
ノート:これは全てのコンテナがサポートしてはいません(例えば WebLogic)。 もし、それがそうでであるべきだと思う場合、あなたのベンダと連絡をとってください。The container provides security for all files below WEB-INF. This applies to client requests, but not forwards from the ActionServlet. Placing all JSPs below WEB-INF ensure that they are only accessed through Actions, and not directly by the client or each other. This allows security to be moved up into the Controller, where it can be handled more efficiently, and out of the base presentation layer.コンテナはWEB-INFよりの下の全てのファイルにセキュリティを提供しています。 これは、クライアントリクエストに適用されます、しかしActionServletからのforwardではありません。 WEB-INFより下に全てのJSPを置く事で、クライアントまたは他のものから直接ではなく、 Actionを通してのみアクセスできる事を保証します。 これは、セキュリティが、プレゼンテイション層の外でより効率的に処理できる場所である、 コントローラの中へ移動する事を可能にします。
However, if your Java ServerPages need to access a number of other HTML resources, like images and stylesheets, it may be more convenient to leave them in the document root. If you are using Link only to Actions, then the address of your JSP's is never exposed anyway, and this strategy loses much of its value. Link only to Actions is usually sufficient.しかしながら,あなたの Java ServerPagesがイメージやスタイルシートとのような、 多くのほかのHTML資源にアクセスする必要がある場合、 ドキュメントルートの中にそれらを残すことは、より便利かもしれません。 もし、Link only to Actions(Actionだけにリンクする)を使うなら、あなたのJSPのアドレスは決して剥き出しにはなりませんので、 この戦略はその価値の多くを失います。 Link only to Actions(Actionだけにリンクする)で通常は十分です。
Use the ActionMapping Parameter property to distinguish operations
When more than one mapping leads to an Action, the standard Parameter property can be used to determine which mapping was dispatched.
オペレーションを区別するために、ActionMapping パラメータ属性を使用する
1つ以上のマッピングがActionを導いている場合、 標準のパラメータ属性は、どのマッピングがディスパッチされたかを決定するのに使われます。
- <action path="/Entry"
type="org.wxxi.polls.http.Client"
name="pollForm"
validate="false"
parameter="input">
<forward name="continue" path="/WEB-INF/pages/Entry.jsp"/>
</action>
- <action path="/Store"
type="org.wxxi.polls.http.Client"
name="pollForm"
validate="true"
input="/WEB-INF/Form.jsp"
parameter="insert">
<forward name="continue" path="/WEB-INF/pages/Results.jsp"/>
</action>For the best flexibility, also retain the "action" property in base form class, and check for both when dispatching:
- In the Action ...
- if (("insert".equals(dispatch)) && (errors.empty())) { ...
最良の柔軟性のために、基本フォームクラス内で“action”属性をさらに保有し、 ディスパッチする時に両方チェックしてください。
- // -- Check request and mappings for dispatch token
String dispatch = request.getParameter("action");
if (dispatch==null)
dispatch = mapping.getParameter();Organize ActionMappings into a command structure
ActionMappingをコマンドへ組込んだ構造
It's been said that query strings are the universal command line. This is especially true of ActionMappings, which usually lead to some type of dynamic operation. Since ActionMappings can be named in any convenient manner, name them after the command structure (which may also be embedded in the menu system). A set of mappings for a file management system might read "file/Save", "file/Open", and "file/Exit", regardless of how the source tree or JSP tree is organized.
クエリストリングが一般的なコマンドラインであると言われています。 これは、あるタイプの動的なオペレーションを引き起こす、ActionMappingにおいては、とくにあてはまります。 ActionMappingsは、どのような便利な方法ででも名付けられるので、 コマンド構造(メニューシステムに組込まれているかもしれません)にしたがってそれらを命名してください。 ファイル管理システム用の1セットのマッピングは、 JSPツリーやソースツリーがどのように構成されているかにかかわらず、 “file/Save”,“file/Open”“file/Exit”と書いてあるかもしれません。Use Global Forwards for all entry points to application
アプリケーションの全てのエントリーポイントにGlobal Forwardを使うAll links in the application should be to ActionMappings. Rather than embed the path to an ActionMapping in a JSP, a link should refer to a Global Forward instead. The Global Forward can include parameters as needed, and should use a logical name that follows the command structure. Compound or path-like names can be used as preferred.
アプリケーション内の全てのリンクはActionMappingにあるべきです。 JSPの中にActionMappingへのパスを埋め込むのではなく、 その代わりにリンクはGlobal Forwardを参照すべきです。 Global Forwardは必要なパラメータを含む事ができるので、 コマンド構造に従った論理名を使用すべきです。 複合語またはパスに似た名前を好んで使えます。<forward name="itemSearch" path="/do/item/Search"/>
<forward name="itemFindDonor" path="/do/item/Search?column=donor"/>
<forward name="itemFindScript" path="/do/item/Search?column=script"/><forward name="item/Search" path="/do/item/Search"/>
<forward name="item/Find/Donor" path="/do/item/Search?column=donor"/>
<forward name="item/Find/Script" path="/do/item/Search?column=script"/><forward name="item.Search" path="/do/item/Search"/>
<forward name="item.Find.Donor" path="/do/item/Search?column=donor"/>
<forward name="item.Find.Script" path="/do/item/Search?column=script"/>This also allows the parameters to be changed without modifying the JSPs or Actions that use the Global Forward.
これは、また、Global Forward を使った Action または JSPを修正せずに、パラメータの変更を可能にします。Very early releases of Struts required subclassing the ActionMappings if you wanted to use names other than "success" and "failure". The mappings have long since been upgraded so that any arbitrary name can be used.
Strutsのとても早期のリリースでは、“success”および“failure”以外の名前を使用したい場合、 ActionMappingnのサブクラスを要求しました。 どんな任意の名前も使用することができるように、マッピングはずっと前に改良されました。Organize ApplicationResources to follow ActionMapping namespaces
Rather than re-invent the wheel, the ApplicationResources and ActionMappings can often share the same naming conventions, or namespaces. The label for the name property in the accounts package may be named accounts.name in the Application Resources.
ActionMappingネームスペースに追従するようにApplicationResourcesを組織化する
わかり切ったことを最初からやり直すのではなく、 ApplicationResourceおよびActionMappingは、 しばしば同じ命名規約あるいはネームスペースを共有することができます。 accountsパッケージのname属性のためのラベルは、Application Resourceにおいて、 accounts.nameと名付けます。Distribute Struts-config.xml and ApplicationResources.properties
In a team environment split up the Application Resources and Struts Configuration files and concatenate them during the Ant build process. So long as each team has their own "namespace", then the properties will not conflict. This allows each team to make changes to their area without stepping on each other.
Struts-config.xmlとApplicationResources.propertiesを分割
チーム環境の中では、Application ResourcesとStrutsコンフィグレイションファイルを分割し、 Antビルドプロセスの中でそれらを連結します。 そして、個々のチームが彼ら自信の“ネームスペース”を持っている限り、属性は矛盾しません。 これは、各チームが互いに影響を与えずに、個々のエリアに変更を加えることを可能にします。The DTD for Struts 1.0 does not support joining multiple config files, but a patch is available.
Struts 1.0のDTDは、複数のコンフィグファイルを結合することをサポートしてはいません。 しかし、 patch があります。 ("http://www.mail-archive.com/struts-user@jakarta.apache.org/msg10052.html)Define and subclass ActionForms to follow the ActionMapping structure
Each ActionMapping can optionally specify an ActionForm. When several related ActionMappings are used with a single Action, define a single ActionForm to use with the related mappings. The mapping is passed to the validate() and reset() methods, and can be used to determine which set of properties to validate or reset. Alternatively, define a subclass for each mapping, and subclass validate() and reset() instead.
ActionMapping構造に追従するようにActionFormのサブクラスを定義する
個々のActionMappingによりActionFormはオプションで指定できます。 いくつかの関連したActionMappingsが単一のActionによって使われる場合、 関連したマッピングによって使うために、単一のActionFormを定義してください。 マッピングはvalidate()とreset()メソッドに渡されます。 そして、validateまたはresetのための属性セットの決定に使われます。 かわりに、それぞれのマッピングへのサブクラスと、サブクラスのvalidate()とreset()を定義してください。
(訳者:ActionFormは使いまわすな!って事かぁ?)Validate domain type and constraints on all user input 全てのユーザインプットにおいて、制約とドメインタイプを有効にする
The optional ValidatorForm can be used to confirm the type and range of the properties input to a form. These are defined in a separate XML file, using regular expressions or custom techniques. Helper classes can then be written that assume that properties passed to them will convert cleanly to required types, and streamline processing. The ValidatorForm class includes custom tags that can also write Javascript for client-side validations.
オプションのValidatorFormは、フォームへの属性入力のタイプと範囲を確認するために使用できます。 これらは、個別のXMLファイルにおいて、正規表現またはカスタムテクニックを使って定義されます。 ヘルパークラスは、それらに渡された属性が、要求されたタイプにクリアに変換されるだろうと仮定し書く事ができます。 そして、処理を効率化してください。 VAlidatorFormクラスは、クライアントサイド確認のためのJavascriptも書く事ができるカスタムタグを含みます。Submit Once, Validate Twice
The general thinking is that there are at least two levels of validation. First, there is the simple domain-type checking, such as fields that are suppose to be numeric should contain only numerals. Second, there is business-logic checking, like invoice numbers are all greater than 1000 or no start dates should occur before 1984, or that the username and password match.
サブミットは1回、確認(Validate)は2回
一般的な考えは、確認(Validate)には最低2つのレベルがあることです。 第一に、“数字だけを含んでいるはずのフィールドである”などの簡単なドメイン型チェックがあります。 第二に、送り状番号のように全ては1000より大きい、とか、開始日が1984年より前になりえない、 とか、ユーザ名とパスワードのマッチのようなビジネスロジックチェックがあります。The first type is easy to automate and doesn't require access to the business logic, so we have a standard method that you can override if you want to do that as part of your ActionForm. With that method, therewould not be a good place for you to plug-in simple validations.
最初のタイプは自動化しやすく、ビジネスロジックへのアクセスを必要としません。 従って、もしあたなのActionFormの一部としてそれをしたいならば、 私達は、あなたがオーバライドできる標準のメソッドを持っています。 そのメソッドは、シンプル確認(Validation)プラグインで、あなたにとっては好ましい所にはありません。The second type gets to be application specific, and is usually handled in the Action perform method. Since you are already overriding perform, there didn't seem to be much value in providing a yet another method to override here.
2番目のタイプは、アプリケーション特有となり、Action performメソッドで通常処理されます。 あなたはすでに perform (メソッド) をオーバーライドしているので、 ここ(perform メソッド)をオーバーライドしたことに対し、 さらに他のメソッドを提供することは、価値がないように思う。Use a "populate" utility to transfer data to value object beans
A problem with writing flexible helper action classes is populating objects of the correct type. One solution is to define a utility class that can convert an Integration layer object into a
Value Object Beansにデータを転送するには、“populate”ユーティリティを使用するMapthat can be used with BeanUtils.populate()to transfer data to an arbitrary bean. The bean must simply have one or more accessors that match column names in theMap(which can be generated from aResultSet, for example). The columns that have a matching accessor will be used to populate the beans. Other accessors and columns are ignored.
柔軟性のあるヘルパActionクラスを書くには、正しいタイプでオブジェクトが存在するのは問題です。 1つの解決策は、任意のBeanへデータを転送するのに、 BeanUtils.populate()が利用可能なマップに変換する、ユーティリティクラスを定義する事です。 Beanはマップ(例のように、ResultSetから生成できます)内のカラム名と対応している、 1つ以上のアクセサを単にもっていなくてはなりません。 一致するアクセサを持ったカラムは、Beanを設定するのに使われるでしょう。 他のアクセサおよびカラムは無視されます。Here is an example method using a ResultSet:
これは、ResultSetを使ったメソッドの例です:public static void populate(Object bean,ResultSet resultSet)
throws SQLException {
// Build a list of relevant column properties from this resultSet
HashMap properties = new HashMap();
// Acquire resultSet MetaData
ResultSetMetaData metaData = resultSet.getMetaData();
int cols = metaData.getColumnCount();
// Scroll to next record and pump into hashmap
if (resultSet.next()) for (int i=1; i<=cols ; i++) {
properties.put(metaData.getColumnName(i),
resultSet.getString(i));
}
// Set the corresponding properties of our bean
try {
BeanUtils.populate(bean, properties);
} catch (Exception e) {
throw new SQLException("BeanUtils.populate threw " + e.toString());
}
}A base bean object can then be passed to an Integration layer helper:
ベースBeanオブジェクトは、この時、統合レイヤーヘルパに渡されます:public static final void selectBean(String key, String command, Object bean)
and the data can be transferred from within the data access method
そして、データはデータアクセスメソッドによって転送されます// Transfer ResultSet (using reflection and metadata)
ResultSetUtils.populate(bean,resultSet);In this case, the command and key string could retrieve many different sets of data, without creating specialized helpers for each set. A bean appropriate to the result is passed with the call, and reflection does the rest.
この場合、コマンドとキーストリングは、各セットに対する特別なヘルパを作らずに、 データの様々なセットを検索できます。 結果に適切なBeanは、呼び出しによって渡され、リフレクションは残りを行います。Here's another method that can be used to return a
Collectionof instantiated beans, each representing a row from theResultSet.
ここには、ResultSetのそれぞれの列を表す実体化したBeanのコレクションの戻りを使う、別の方法があります。
public static Collection getCollection(Object target, ResultSet resultSet)
throws SQLException {
// Acquire resultSet MetaData
ResultSetMetaData metaData = resultSet.getMetaData();
int cols = metaData.getColumnCount();
// Use ArrayList to maintain ResultSet sequence
ArrayList list = new ArrayList();
// Acquire target class
Class factory = target.getClass();
// Scroll to each record; copy to hashmap; populate target
while (resultSet.next()) {
HashMap map = new HashMap(cols,1);
for (int i=1; i<=cols; i++) {
map.put(metaData.getColumnName(i),
resultSet.getString(i));
}
// Create bean and populate from hashmap
try {
Object bean = factory.newInstance();
BeanUtils.populate(bean,map);
list.add(bean);
} catch (Exception e) {
throw new SQLException("RequestUtils.getCollection: " + e.toString());
}
}
return ((Collection) list);
}Like the other method, this can be called from within a Helper action to disengage the Resource Layer from the rest of the application. All that is required is to pass along an instance of a bean which has some accessors that some column names in the ResultSet.
他の方法のように、リソースレイヤをアプリケーションから切り離すために、ヘルパAction内で呼ばれます。 必要な全ては、ResultSet内のいくらかのカラム名のアクセサをもったBeanのインスタンスに渡すことです。This is a good way to reuse beans for both pick-lists and detail pages. The detail bean can be used for both, but passed to a different query that only populates properties used on the pick-list. Since the reflection runs from the result set to the bean, you can use fewer columns than may be available on the bean. Unused properties will simply be null.
これは、ピックリストと詳細ページの両方のBeanを再利用するよい方法です。 詳細Beanは両方で利用することができます。 しかし、ピックリスト内で使用される、単にセットされるプロパティの異なる要求 がパスされます。 結果セットからBeanへのリフレクションが実行されるので、 Beanで利用可能な少ないカラムを使用します。 未使用のプロパティは無効となります。Create convenience objects for commonly used attributes
共通で使う属性のためにConvenience Objectを作るInside of a Web application, there often several attributes that need to be used throughout the application. These can include a user profile, bookmark, menu settings, and other properties. Rather than manage each of these separately, a convenience object can be defined to wrap these together. While the objects as a set may not be cohesive, it may be convenient to handle them as a single API.
WebApplicationの内部では、アプリケーション全体にわたって使用される必要がある、 いくつかの属性がしばしばあります。 これらは、ユーザプロファイル、ブックマーク、メニューセッティングとほかの特性を含むことができます。 これらのそれぞれを別々に管理するのではなく、 Convenience Objectとして、これらを共に包み定義することができます。 セットとしてのオブジェクトに結合力のない間は、 単一のAPIとして、それらを扱うことは便利かもしれません。Check logins at the Controller or Action level
コントローラまたはActionのレベルでログインをチェックするWhen pages are placed under WEB-INF, you can make all security checks at the Action or controller level, and forgo putting any type of "isLoggedIn" tag on your pages. The page should be able to assume that the Action authorized the user before forwarding control (since the user could not access the page except through an ActionForward).
ページがWEB-INFの下にあるとき、 Actionまたはコントローラのレベルで全てのセキュリティチェックを行なうことができます。 そして、あなたのページに任意のタイプの"isLoggedIn”タグを置くのを断念します。 ページはActionがコントロールを進める前に、 ユーザを認証したと仮定することができるべきです。Use multifunction Actions to service related ActionMappings
ActionMappingに関連したサービスには、マルチファンクションActionを使うHere's an example:
ここで例をしめします。In an Insert / Update workflow, there are usually three ActionMappings, one for "add", to return an empty ActionForm, one for "edit", to return a populated ActionForm, and one for "store" to either insert or update
the ActionForm properties into the model. Usually, all three of these are mapped to the same Action. A good way to tell them apart is to use the parameter property with your mapping.
追加/更新 の業務の流れの中では、通常3つの ActionMapping があります。 一つは 追加("add")でこれは空の ActionForm を返します。もう一つは編集 ("edit")で、値が詰められた ActionForm です。最後は ActionForm内のプロ パティをモデルに挿入する "store" があります。たいていの場合、これら3つ は同じ Action にマッピングされることになります。これらを分割する良い方 法としては、 mapping に parameter プロパティを使用することです。
<!-- Input Article (admin) -->
<action
path="/admin/article/Add"
type="org.apache.struts.artimus.http.Client"
name="articleForm"
scope="request"
validate="false"
parameter="add">
<forward
name="continue"
path="/WEB-INF/pages/Entry.jsp"/>
</action>
<!-- Article Edit action mapping (admin) -->
<action
path="/admin/article/Edit"
type="org.apache.struts.artimus.http.Client"
name="articleForm"
scope="request"
validate="false"
parameter="select">
<forward
name="continue"
path="/WEB-INF/pages/Entry.jsp"/>
</action>
<!-- Article Save action mapping (admin) -->
<action
path="/admin/article/Save"type="org.apache.struts.artimus.http.Client"name="articleForm"
scope="request"
validate="true"
input="/WEB-INF/pages/Entry.jsp"
parameter="save">
<forward
name="continue"
path="/WEB-INF/pages/Article.jsp"/>
</action>The Action can the call mapping.getParameter() and switch to the appropriate "sub-Action". When "add" is called, the Action checks the parameter, and returns a blank form. When the parameter is "edit", the Action uses other properties on the form to retrieve a record and populate the form.
Actionは、mapping.getParameter()を呼び出すことで、 適切な"sub-Action"に切り替える事ができます。 "add"が呼ばれたとき、Actionはパラメータをチェックします。 そして、空のフォームを返します。 パラメータが"edit"の場合、 Actionはレコードを取り出し、フォームに値を詰める処理を行った後のフォームのプロパティを使用しますWhen the parameter is "save", the Action examines properties on the form to determine whether it needs to add or update the records. Usually, a record needs a primary key, which is null on an add and not null on update, and this is an easy way to tell which kind of save is needed.
それがレコードを加えるまたは、更新する必要がるかどうかを決めるために、 そのActionはForm内で属性を検討します。?? 通常は、レコードはプライマリ・キーを必要とします。 追加にnullがあるばあい、 パラメータが"save"のときは、 Action は、レコードを追加および更新する必 要があるかどうかを調べるためにフォーム内のプロパティを調べます。たいて いは、レコードはプライマリキーを必要とします。プライマリキーは追加の時 は null で、更新の時は null ではないので、どのような保存(訳:追加か更新か、 ということです)が必要かという情報を得るのは簡単です。Look at DispatchAction for a model of creating multi-function Actions
The optional DispatchAction class in the Struts Action package allows you to create several methods with the same signature of perform(). Given the right parameter, it can then automatically switch between these methods, which all act just like independent copies of perform().
マルチファンクションActionのモデルへのDespatchActionを見るStruts ActionパッケージのオプションのDispachActionクラスは、perform()の同じシグネーチャによって、 いくつかのメソッドを作成することを可能とします。 正しいパラメータを与えると、ちょうど、独立したperform()のコピーのように全てがふるまい、 メソッド間で自動的に切り替えを行う事ができます。
In practice, related functions can also share much of the Struts error-handling and control code, so creating new perform-like methods can be more work than it should be. However, the principal is sound, and a good approach to multi-function Actions is to put each function in a private method that returns a control value, or an array of error tokens. As long as all needed properties are passed on the stack, and class properties are not used, then the methods will be thread safe.
実際には、関連した機能は、Struts エラーハンドリングと制御コードの多くも共有できます。 したがって、新しいperformのようなメソッドを作る事は、より多くの作業でありえます。 しかしながら、重要なのは堅実なことです。 そして、多機能アクションへのよいアプローチは、 コントロール値、またはエラートークンの配列をリターンするプライベートメソッドに それぞれの機能を配置することです。 (訳者補足:1機能1プライベートメソッドでAction派生クラス内にコードを書け、 メソッドのリターン値を見てその1機能が正常に機能したか判断しろ) 要求されたプロパティ全てがスタックに渡されれる限りは、クラスのプロパティは使われず、 そのときはそのメソッドはスレッドセーフになります。 (訳者補足:Actionクラスのインスタンスは使いまわされる為、 そのメソッドはスレッドセーフで実装する必要があるからです)
// :TODO: CRUD example
Use a Base Action for your application
アプリケーションにはBese Actionを使うOften, the Actions in your application will need to perform some basic tasks. To ensure these tasks are implement consistently, create a base Action for the others in your application to subclass.
しばしば、アプリケーション内のアクションは、いくつかの基本的なタスクを実行する必要があるでしょう。 これらのタスクが一貫して実装されることを保証するためには、 サブクラスに、アプリケーションにおける他の基本的な動作を作成します。
If key tasks needs to be completed in each Action's perform() method, a good approach is to create a new abstract methods for your subclasses to use in lieu of perform(). The base Action class does its business, and if all is well, then returns the result of the new method. This allows you to change the signature of your new method, if there is ever any reason for that, and provides a cleaner flow.
もし、鍵となるタスクが、個々のアクションのperform()メソッドの終了を必要とするなら、 よいアプローチは、サブクラスがperform()の代わりに使用するための、 新しいabstractメソッドを作ってください。 ベースアクションクラスは業務を行い、そして全てが適切な場合、 新しいメソッドの結果を返します。 これは、どんな理由があるにせよ、 新しいメソッドのシグネーチャを変更するすることを可能にし、 簡素なフローを提供します。
public ActionForward perform(ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {
// Application specific behaviour
// if everything is kosher call subclass
return ( performAction(mapping,form,request,response, myParameter ) )
}where performAction is an abstract method of the base class.
ここで、performActionはベースクラスのabstractメソッドです。.
Provide session management for cached resources
Most Web applications require use of a workflow, and need to save ActionForms and other attributes for future use. These items may also need to be released in a block when the workflow completes. A convenient way to handle this is to define a "Resource Cache". This can simply be a hash map that uses a wrapper object to manage its items. The wrapper object can include a named "scope", a property to indicate whether the item is permanent or temporary, and a counter. To keep the cache manageable, a threshold can be set, and the oldest temporary items removed when the threshold is reached and a new item needs to be added.
キャッシュされたリソースにセッション管理を提供するExtend ActionServlet to support your API
The Struts ActionServlet can be extended to provide application-specific methods. These methods can streamline tasks like
あなたのAPIをサポートするには、ActionServletを拡張する
- Checking a user's logon state and rerouting as appropriate,
- Accessing, modifying, and resetting application-specific objects,
- Codifying your practices, like logging exceptions and saving them under a common request attribute.
Using cached forms, bookmarks, and key notifiers to implement workflows
Tasks that must be completed over more than one request (or step) can be difficult to manage in a Web application. A good approach is to setup a session manager that allows you to save attributes under a named scope, and then release them all at once. This makes it relatively easy to save information between steps, retrieve information from another step, and make it all go away when you are done. There is a new workflow proposal between Struts and the Jakarta Commons that uses a more sophisticated version of this approach.
ワークフローを実装するために、cached form、bookmarkとkey notifierを使う。// :TODO: Workflow snapshot
Another helpful component for workflows is a "bookmark". This is a stored URI that will return the user to a particular page in the application. If a user exits a workflow, the exit page can be bookmarked for later return. A custom tag can be made part of the template which looks for the bookmark and displays a button to return to the bookmarked page. When a bookmark is not present, then the tag does not display the return form. This can also be used to return to a page after linking to a help area.
Many forms in a system have a key field that is used by other forms as a foreign key. When implementing wizards, setup a key notification system. Here, the Actions post the primary key for a form whenever it is accessed. (An abstract getKey() accessor on your base ActionForm is helpful here.) Other forms that use this key as a foreign key can then use the latest value to prepopulate their own related property (when null). The result is that if a user goes off to edit or add a record to complete one workflow, and the key for the related record is logged. When they return to the first workflow, the foreign key they found or added is now inserted into the form.
Use the Action Token methods to prevent duplicate submits
There are methods built into the Struts action to generate one-use tokens. A token is placed in the session when a form is populated and also into the HTML form as a hidden property. When the form is returned, the token is validated. If validation fails, then the form has already been submitted, and the user can be apprised.
複製防止のために、Action Tokenメソッドを使う
- saveToken(request)
- on the return trip,
- isTokenValid(request)
- resetToken(request)
Define a base ActionForm for your application with workflow properties
In practice, many applications need to use standard hidden properties to manage workflows, which are best put in a base ActionForm class. Some common properties needed are
ワークフロー特性をもつアプリケーションには、base ActionFormを定義する
- getKey() - the value of the primary key for this form
- getDispatch() (or getAction) - a value used by the Action to route, or dispatch, the form
- getPage() - for managing workflows with a ValidatorAction
Use the LabelValueBean as a core value object class
When a good number of drop-down controls are being used, standardize on the LabelValueBean object provided in the Struts example. This is an easy way to create both the Label and Value collections the Struts options tag expects.
コアValue Object クラスとして LavelValueBeanを使うDefine a standard ResultBean for delivering value objects to the presentation layer
In practice, most pages require the display of more than one element. Usually there are standard elements that most request pages display, like a subtitle or the search parameters. These can be encapsulated in a helper bean along with a property for an iterator, when set of beans needs to be displayed (e.g. in a table).
Value Objectをプレゼンテイションレイヤに転送するための、標準のResultBeanを定義する// :TODO: Sample from ext.sql.AccessBean
Use request.isUserInRole() to implement role-based presentation logic
The request tag extension in Jakarta Taglibs exposes the RequestUtil bean, including the isUserInRole method. This is an excellent way for the presentation layer to determine what gestures can be made by what users, and provide the relevant controls. Since the controller layer is determining the content of the underlying bean, and the presentation layer is simply reading it, the integrity of the tiers is intact.
ロールベースの表示ロジックを実装するために、request.isUserInRole()を使うFor customized security, place a role bean in session for access by custom tags and other resources
The standard security model is exposed through methods on the RequestUtils bean. If your security system is not fully integrated with the standard model, provide your own bean in the session with similar methods, or extend the ActionController to add this bean to each request. Custom tags, like those provided by the Jakarta Tagibs request library, can then access your bean instead of the standard RequestUtil bean.
カスタマイズされたセキュリティのために、カスタムタグおよび他のリソースからアクセスするために、 ロールBeanはセッションに置く。Do not check for null ActionForm beans in your Actions
If an Action expects an ActionForm bean, then its API contact with the ActionMappings should require that this bean, or a subclass, be named in the ActionMapping. The Actions contact wit the controller is that it will always instantiate the bean before the Action is called. If either contact is broken, the application should expose a null pointer exception so that the problem is fixed and the misunderstanding resolved. Whether an Action expects an ActionForm bean should be specified in its Javadoc.
Actionにおいて null のActionForm beanはチェックしないDevelop a library of source code skeletons and sample classes
The classes for frameworks like Struts always have several dependencies and standard methods that must be subclassed. It's important to provide developers with skeletons for these classes, that are specifically designed for this use. Otherwise, people are continually picking a likely Action and spending valuable time creating a skeleton on-the-fly. Use of a formal skeleton library gives you a place to insert new features as they become available. Skeletons should also include starter Javadocs to encourage developers to maintain these.
サンプルのクラスとソースコードのスケルトンのライブラリを開発するActionForm skeletons may also include sample property names, that developers can quickly change using search and replace. Since an ActionForm, Action, and JSP often travel together, a boilerplate file can be provided that allows a developer to setup all three with a single search/replace pass. This should be followed by an editor macro that finds the get and set methods and capitalizes the next letter (getthis -> getThis).
// :TODO: Example from Stub
A samples folder can also be a place where developers can save copies of classes that use important features, and make be made into skeletons at some point. Compared to the skeleton library, the samples library is a scrapbook, where the skeletons are blank forms.
Development Cycle
One effective approach to building a Struts application is this:
開発サイクル
- Develop the screen requirements in terms of their structure. We are not thinking about HTML chrome at this point, but specifying what data entities key screens must collect or display. This sometimes may imply a workflow, but at this point we are looking at what data we need to collect and display, rather than organizing how it is presented, screen by screen.
- Develop helper classes to meet screen requirements. This will often mean also defining value object beans to hold the data required by the screens, and passed through the helpers.
- Develop the work flow requirements - At what point to people login, what happens after they do, and so forth. This can often result in an extended flowchart or storyboard.
- Define a system of ActionMappings to meet the work flow requirements. This can result in a command hierarchy which could also be represented by a set of dropdown menus.
- Create stub JSPs to demonstrate workflows using the ActionMappings. Here it can be helpful to use "stub" ActionForms and Actions that use a dummy set of properties, like property1, property2, and so forth. When the final properties are known, these can be renamed.
- Develop Actions to be called by the ActionMappings, which in turn call the appropriate helpers and forward to requisite JSPs.
- Update stub JSPs to use production properties.
Use /do/* for Struts ActionServlet mappings (rather than *.do)
Struts ActionSerlvetのマッピングは、 *.do よりは /do/* を使え
- Some Actions may return dynamic pages of a particular type, like spreadsheets, PDFs or text files. When this is done, it may be preferable to use the extension for the file type returned. For example, an Action URI like [ /do/reports/quarterly.xls ] may generate a spreadsheet response that the user can view in Excel.
- Note that a URI like [ /do/reports/quarterly.xls ] does not work as the target of the action property in a html:form tag (at this time). An URI like this can be used as the target of a hyperlink.
- *.do correlates with *.jsp, but Actions are not JSPs (or even servlets). Extension mapping encourages developers to think of Action URIs to be like a path rather than like a method signature.
- Some proxy servers choke on query strings. An enhancement to the Struts ActionMappings could use "extra path information" instead, and eliminate query strings altogether.
- /do/ looks ~way~ cooler on the address bar
Other Suggestions
These are not Struts-specific, but can apply to any project using Struts.
Adopt coding standards
Using a coding standard is often more important than which one you use. The essential requirement is that the standard is clearly documented. One such standard is described in The Elements of Java Style.
Organize the code tree around tiers and the standard packages
The careful placement of a class within a package structure can make classes easier to find and use, and also send developers a signal about how the classes are used in relation to each other. By clearly placing "business" tier code and "presentation" tier code on parallel branches, the tiers become a natural part of the developer's frame of reference.Organize the code tree around teams
Within the branch of the source tree, it is often useful to organize around the command structure, which often follows the delegation of labor within a team. The command structure through a dropdown menu may read "accounts/Add", "accounts/Update", and "accounts/Search". This could lead to a source tree with an accounts package with Add, Update, and Search classes, which is managed by the Accounts team members.Require documentation of all properties and methods, public and private
The fastest way to develop robust applications is for developers to thoroughly document their code using the standard Javadoc utilities. This helps developers focus on the quality of the code they write, and the importance of communicating their ideas to other workers. This is especially important when team members are not working at the same location, or do not share the same native language.Provide a developer's portal page with links to common resources.
This is something every developer needs, even when they are working alone, and how More About Struts got started. Here are some likely items for a Struts developers portal:
- PDFs for JavaBean, Servlet, and JSP specifications
- Struts Javadocs and User Guide, Struts list archive, More About Struts
- JUnit, Ant, and Log4J documentation
- Internal resources: Portal Javadoc, code samples, Bugzilla, et cetera.
- Links: java.sun.com, Jakarta Taglibs, JGuru,
Install a formal bug and feature tracking package, like Bugzilla
For any project involving three or more developers, it's worthwhile to install something like Bugzilla for internal use, to help keep everyone on the same page. This can also be used to manage work assignments, where a team lead delegates a task by assigning the feature request to another member of the team.Implement an automatic nightly build, deploy, document, and test routine
To move into rapid deployment mode, it is important that team members get feedback as to how their work interacts as soon as possible. The best way to do this is to build and deploy the latest CVS nightly, along with updated Javadocs. The unit tests should then be run to ensure that new changes have not broken any existing contracts.Maintained by
Ted Husted,
Husted dot Com,
Tel +1 716 737-3463,
http://www.husted.com/about/strutsRelease .04b
Ted Husted is an independent software consultant specializing in developer training, code reviews, refactoring, and confidential priority support. For more information, visit the Husted dot Com Web site.
TODO
- ActionForm semaphores
- Tiers and Unit Testing / Actions are adapters
- JavaBeans are core objects.
- ActionForms are only meant to be used with HTML form tags.
- ActionForms may convert Strings to other types for transfer to internal objects.
- The default ActionForm properties should be considered immutable, and set only by the actor or when the form is created.
- Data Access objects should be used to encapsulate interactions with the model.
- RowSets are a good tool for implementing data access methods.
- Value Objects should be used to store data retrieved from the model.
- The Action should coordinate moving data from ActionForms to Value objects, and moving data from Data Access objects to Value objects.
- Value objects may call specific Data Access objects internally and expose generalized methods for other objects, like the Action, to call as needed.
- Data access objects and value object may share the same data store.
- Business logic should be encapsulated with interfaces and implemented by value objects or other internal-use beans.
- Deliver the state of your application to JSPs as JavaBeans and other objects that can be accessed by custom tags.
Scriplets are a stop gap and should be avoided.
| Translated by | Yukimitsu Kurozumi Hideyuki Kagami |