JWorld@TW the best professional Java site in Taiwan
      註冊 | 登入 | 全文檢索 | 排行榜  

» JWorld@TW » Web Framework » Struts  

按列印兼容模式列印這個話題 列印話題    把這個話題寄給朋友 寄給朋友    訂閱主題
reply to topicthreaded modego to previous topicgo to next topic
本主題所含的標籤
無標籤
作者 [小程式分享] 簡化的 Struts Dispatch Action [精華]
ingramchen

Web monkey



發文: 479
積分: 12
於 2005-05-17 22:02 user profilesend a private message to usersend email to ingramchenreply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
Struts 的 Dispatch Action 已經行之有年了,有 Dispatch, Lookup, MappingDispatch... 好幾種可以用。但設定方法都很囉嗦 (網頁,程式,struts-config三者都要 hard-code String 在上面),而且適用的了 A ,就不能用在 B。例如雖然 DispatchAction 可以寫在 URL 上: /foo.do?method=saveOrder ,但是遇到一個 form 需要利用多個 submit button dispatch 時,就不行了。你得轉用 LookupDispatch,可是 LookupDispatch 又需要在 Action 定義一個很醜的 getKeyMethodMap() (裡面全部是 hard-code string)。接下來要分享一個通用 URL/submit button/struts-config.xml 三者設定的 SimpleDispatchAction:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
package org.bioinfo.util.struts;
 
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
 
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;
 
/**
 * 簡化的 DispatchAction
 * 
 * 使用方法:
 * 
 * 繼承此 class,並定義各個 action method,比方說有兩個 method 分別做儲存和刪除:
 * 
 * <pre>
 * public ActionForward save(ActionMapping mapping, ActionForm form,
 *         HttpServletRequest request, HttpServletResponse response)
 *         throws Exception {
 *   // 儲存的程式碼...
 * }
 * 
 * public ActionForward delete(ActionMapping mapping, ActionForm form,
 *         HttpServletRequest request, HttpServletResponse response)
 *         throws Exception {
 *   // 刪除的程式碼...
 * }
 * </pre>
 * 
 * 有三種方式可以設定 dispatch 到哪個 method 上。
 * 
 * (1) submit button 法,直接寫在 submit 的 property 上:
 * 
 * <code>
 * <html:form action="/some/work">
 *    .... some thing ....
 * 
 *    <html:submit property="dispatch=save" value="儲存"/>
 *    <html:submit property="dispatch=delete" value="刪除"/>
 * </html:form>
 * 
 * 或者如果需要 i18n 的話,可以用 bean:message:
 * 
 * <html:form action="/some/work">
 *    <html:submit property="dispatch=save" >
 *         <bean:message key="button.save" />
 *    </html:submit>
 *    <html:submit property="dispatch=delete" >
 *         <bean:message key="button.delete" />
 *    </html:submit>
 * </html:form>
 * </code>
 * 
 * 注意 property 裡面的值是 'dispatch=xxxx' 記得要寫等號與 method 名稱,而且大小
 * 寫要對,不能空白。當網頁按下 "儲存" 時,則會執行 /some/work.do 的 save(...) 的  
 * method。 如果按下 "刪除" 則執行 delete(...),
 * 
 * 建議 -- 這種寫法通常是用在一個 Action 有多個 dispatch method,而每個 method 都共
 *           用同個 ActionForm
 * 
 * (2) URL 法,接在 URL 後面:
 * 
 * <code>
 *    <html:form action="/some/work?dispatch=save">
 *     或是用 link 也可以
 *    <html:link action="/some/work?dispatch=save" />
 * </code>
 * 
 * 建議 -- 通常用在不需要 ActionForm 的 Action,或者是要將 submit button 法寫成 url 時使用。
 * 
 * (3) struts-config 法,直接寫死在 parameter='dispatch=foo' 上
 * 
 * <code>
 *   <action 
 *       path="/saveOrder" 
 *       name="SaveOrderForm"
 *       type="antar.order.web.OrderDispatchAction"
 *       parameter="dispatch=save" >
 *   </action>
 *   <action 
 *       path="/deleteOrder" 
 *       name="DeleteOrderForm"
 *       type="antar.order.web.OrderDispatchAction"
 *       parameter="dispatch=delete" >
 *   </action>
 * </code>
 * 
 * 建議 -- 這種寫法通常是為了讓 Action 中每個 dispatch method 使用不同的 ActionForm
 *           。一旦寫死在 struts-config 裡,該 mapping 的 path 就不能與 URL 法 或是 
 *           submit button 法同時使用。
 * 
 * 最後請注意同一個 request 上,URL 法不能與 submit button 法同時使用 (但可以連續 forward):
 * 
 * <code>
 *  ...........錯誤範例...........
 * <html:form action="/some/work?dispatch=save">
 *    .... some thing ....
 *    <html:submit property="dispatch=delete" value="刪除"/>
 * </html:form>
 * </code>
 * 
 * @author ingram
 *  
 */
public abstract class SimpleDispatchAction extends Action {
 
    private static final String SIMPLE_DISPATCH_ACTION_KEY = "__SIMPLE_DISPATCH_ACTION_KEY";
 
    private DispatchMethodHelper dispatchMethodHelper = new DispatchMethodHelper(
            SIMPLE_DISPATCH_ACTION_KEY);
 
    private Class clazz = this.getClass();
 
    private Class[] argTypes = new Class[] { ActionMapping.class,
            ActionForm.class, HttpServletRequest.class,
            HttpServletResponse.class };
 
    private Map dispatchMethods = new HashMap();
 
    public final ActionForward execute(ActionMapping mapping, ActionForm form,
            HttpServletRequest request, HttpServletResponse response)
            throws Exception {
 
        String methodName = dispatchMethodHelper
                .getMethodName(request, mapping);
 
        Method method = null;
        try {
            Object[] args = { mapping, form, request, response };
            method = obtainDispatchMethod(methodName);
            return (ActionForward) method.invoke(this, args);
        } catch (NoSuchMethodException e) {
            throw dealWithMethodProblem(methodName, e);
        } catch (IllegalAccessException e) {
            throw dealWithMethodProblem(methodName, e);
        } catch (InvocationTargetException e) {
            if (e.getCause() instanceof Exception) {
                throw (Exception) e.getCause();
            } else {
                throw new RuntimeException(
                        "invoke " + methodName + "() failed",
                        (e.getCause() != null ? e.getCause() : e));
            }
        }
    }
 
    private RuntimeException dealWithMethodProblem(String methodName,
            Exception e) {
        return new RuntimeException(
                "can not access dispatching method:["
                        + methodName
                        + "]. possible cause is :\n"
                        + "(1) wrong parameter value, the correct format is 'dispatch=methodName' \n"
                        + "        or\n"
                        + "(2) wrong method in your DispatchAction, it should like \n"
                        + "    public ActionForward methodName("
                        + "ActionMapping,ActionForm,HttpServletRequest,HttpServletResponse)\n\n",
                e);
    }
 
    private Method obtainDispatchMethod(String methodName)
            throws NoSuchMethodException {
        Method method = (Method) dispatchMethods.get(methodName);
        if (method == null) {
            method = clazz.getMethod(methodName, argTypes);
            dispatchMethods.put(methodName, method);
        }
        return method;
    }
 
}

使用方式跟 Struts 的 DispatchAction 差不多,不過好處是設定方式可以很多種 (submit button 法,URL 法,struts-config 法),而寫法則通通都一樣,都是 dispatch=xxx。內部的運作說穿了也只是用 reflection 的機制來實作而已,並不難。除了這一個 class 外,還需要另一個 Helper,要跟 SimpleDispatchAction 放在同一個 package 裡:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
package org.bioinfo.util.struts;
 
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
 
import javax.servlet.http.HttpServletRequest;
 
import org.apache.struts.action.ActionMapping;
 
/**
 * A helper to deal with 'dispatch=methodName' in request parameter or Struts
 * config parameter
 * 
 * @author ingram
 *  
 */
public class DispatchMethodHelper {
 
    private static final String KEY_VALUE_SEPERATOR = "=";
 
    private final static String KEY = "dispatch";
 
    private static final String VALID_PARAMETER_NAME_PATTERN = KEY + "\\"
            + KEY_VALUE_SEPERATOR + "[a-zA-Z0-9_]+";
 
    private String keyToSaveRequestParameters;
 
    public DispatchMethodHelper(String keyToSaveRequestParameters) {
        this.keyToSaveRequestParameters = keyToSaveRequestParameters;
    }
 
    String getMethodName(HttpServletRequest request, ActionMapping mapping) {
 
        final List methodNamesFromCurrentRequest = gatherMethodNameFromCurrentRequestParameters(request);
 
        if (methodNamesFromCurrentRequest.size() > 1) {
            throw new IllegalArgumentException(
                    "\nMultiple dispatch request parameter: "
                            + methodNamesFromCurrentRequest
                            + " Only one parameter is allowed.");
        }
 
        if (methodNamesFromCurrentRequest.isEmpty()) {
            if (hasDispatchParameter(mapping)) {
                return parseMethodName(mapping.getParameter());
            }
            throw new IllegalArgumentException(
                    "no 'dispatch' or 'dispatch=methodName' found in parameter");
        }
 
        //methodNamesFromCurrentRequest.size() == 1
        String methodNameFromRequest = (String) methodNamesFromCurrentRequest
                .get(0);
        if (hasDispatchParameter(mapping)) {
            throw new IllegalArgumentException("can not mix ActionMapping '"
                    + mapping.getParameter()
                    + "' with request parameter: dispatch="
                    + methodNameFromRequest);
        }
        return methodNameFromRequest;
 
    }
 
    private boolean hasDispatchParameter(ActionMapping mapping) {
        if (mapping.getParameter() != null) {
            String methodNameFromMapping = parseMethodName(mapping
                    .getParameter());
            return methodNameFromMapping != null;
        }
        return false;
    }
 
    private List gatherMethodNameFromCurrentRequestParameters(
            HttpServletRequest request) {
 
        final List methodNameFromRequestParameters = new ArrayList();
        final String[] values = request.getParameterValues(KEY);
        if (values != null) {
            for (int i = 0; i < values.length; i++) {
                methodNameFromRequestParameters.add(values[i]);
            }
        }
 
        for (Enumeration e = request.getParameterNames(); e.hasMoreElements();) {
            String parameterName = (String) e.nextElement();
            if (!parameterName.matches(VALID_PARAMETER_NAME_PATTERN)) continue;
            for (int i = 0, max = request.getParameterValues(parameterName).length; i < max; i++) {
                String methodName = parameterName.split(KEY_VALUE_SEPERATOR)[1];
                methodNameFromRequestParameters.add(methodName);
            }
        }
 
        List current = substract(methodNameFromRequestParameters,
            getLastMethodFromRequestParameters(request));
 
        setLastMethodFromRequestParameters(request,
            methodNameFromRequestParameters);
        return current;
    }
 
    private List substract(List methodNameFromRequestParameters,
            List lastRequestParameters) {
        List substract = new ArrayList(methodNameFromRequestParameters);
 
        for (Iterator iter = lastRequestParameters.iterator(); iter.hasNext();) {
            String element = (String) iter.next();
            removeElement(substract, element);
        }
        return substract;
    }
 
    private void removeElement(List list, String toRemove) {
        List iteration = new ArrayList(list);
        for (int i = 0, max = iteration.size(); i < max; i++) {
            String element = (String) iteration.get(i);
            if (element.equals(toRemove)) {
                list.remove(i);
                return;
            }
        }
    }
 
    private void setLastMethodFromRequestParameters(HttpServletRequest request,
            List requestParameters) {
        request.setAttribute(keyToSaveRequestParameters, requestParameters);
    }
 
    private List getLastMethodFromRequestParameters(HttpServletRequest request) {
        List all = (List) request.getAttribute(keyToSaveRequestParameters);
        if (all == null) {
            all = new ArrayList();
            setLastMethodFromRequestParameters(request, all);
        }
        return all;
    }
 
    private String parseMethodName(String nameWithDispatchKeyValue) {
        if (nameWithDispatchKeyValue.matches(VALID_PARAMETER_NAME_PATTERN)) {
            return nameWithDispatchKeyValue.split(KEY_VALUE_SEPERATOR)[1];
        }
        return null;
    }
 
}

這個 helper 則是負責處理 dispatch='xxx' 這些字的 parse 及處理,算是做苦功啦,沒啥好提的....
有興趣的朋友可以試試這個 SimpleDispatchAction!

SimpleDispatchAction.zip (8.25k)


browser edited on 2005-06-29 19:36
reply to postreply to post
MyBlog | 訂便當管理系統

作者 Re:[小程式分享] 簡化的 Struts Dispatch Action [Re:ingramchen]
lalala1025





發文: 1
積分: 0
於 2007-03-12 10:23 user profilesend a private message to userreply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
感謝分享,

在公司寫了快一年的struts,都沒發現這件事,
真慚愧,
Black Eye


reply to postreply to post
作者 Re:[小程式分享] 簡化的 Struts Dispatch Action [Re:lalala1025]
ingramchen

Web monkey



發文: 479
積分: 12
於 2007-03-12 18:18 user profilesend a private message to usersend email to ingramchenreply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
這個只能當參考用... 其實有一些 bug...

如果繼續寫 struts 的話,那還是快升到 struts 2 吧


reply to postreply to post
MyBlog | 訂便當管理系統

作者 Re:[小程式分享] 簡化的 Struts Dispatch Action [Re:ingramchen]
alexust





發文: 1
積分: 0
於 2008-10-23 22:07 user profilesend a private message to userreply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
什麼bug?

reply to postreply to post
» JWorld@TW »  Web Framework » Struts

reply to topicthreaded modego to previous topicgo to next topic
  已讀文章
  新的文章
  被刪除的文章
Jump to the top of page

JWorld@TW 本站商標資訊

Powered by Powerful JuteForum® Version Jute 1.5.8