001: /*
002: * Copyright (C) 2006 Methodhead Software LLC. All rights reserved.
003: *
004: * This file is part of TransferCM.
005: *
006: * TransferCM is free software; you can redistribute it and/or modify it under the
007: * terms of the GNU General Public License as published by the Free Software
008: * Foundation; either version 2 of the License, or (at your option) any later
009: * version.
010: *
011: * TransferCM is distributed in the hope that it will be useful, but WITHOUT ANY
012: * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
013: * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
014: * details.
015: *
016: * You should have received a copy of the GNU General Public License along with
017: * TransferCM; if not, write to the Free Software Foundation, Inc., 51 Franklin St,
018: * Fifth Floor, Boston, MA 02110-1301 USA
019: */
020:
021: package com.methodhead.aikp;
022:
023: import java.text.DateFormat;
024: import java.text.ParseException;
025:
026: import java.util.Date;
027: import java.util.Iterator;
028: import java.util.List;
029: import java.util.Map;
030:
031: import javax.servlet.http.HttpServletRequest;
032: import javax.servlet.http.HttpServletResponse;
033:
034: import com.methodhead.auth.AuthAction;
035: import com.methodhead.util.StrutsUtil;
036:
037: import com.methodhead.persistable.PersistableException;
038: import com.methodhead.MhfException;
039:
040: import org.apache.log4j.Logger;
041:
042: import org.apache.struts.action.ActionForm;
043: import org.apache.struts.action.ActionForward;
044: import org.apache.struts.action.ActionMapping;
045: import org.apache.struts.action.ActionMessage;
046: import org.apache.struts.action.ActionMessages;
047:
048: import org.apache.struts.validator.DynaValidatorForm;
049: import org.apache.struts.action.DynaActionForm;
050: import org.apache.commons.beanutils.DynaProperty;
051: import com.methodhead.util.StrutsUtil;
052: import com.methodhead.util.OperationContext;
053: import com.methodhead.auth.AuthUser;
054: import com.methodhead.auth.AuthUtil;
055: import org.apache.commons.lang.StringUtils;
056:
057: /**
058: <p>
059: An Struts action with which to build a web interface for {@link
060: com.methodhead.aikp.AutoIntKeyPersistable
061: AutoIntKeyPersistable}s. This action will perform the following
062: operations: new, edit, save, and delete. Methods for these operations
063: and a number of support methods can be overloaded to achieve more
064: sophisticated behaviour.
065: </p>
066: <p>
067: Using this action requires at least this Struts configuration:
068: </p>
069: <xmp>
070: <form-bean
071: name ="yourObjectForm"
072: dynamic="true"
073: type ="com.methodhead.aikp.AikpForm">
074:
075: <form-property name="action" type="java.lang.String"/>
076: <form-property name="id" type="java.lang.String"/>
077: <form-property name="submit" type="java.lang.String"/>
078: <form-property name="cancel" type="java.lang.String"/>
079: <form-property name="delete" type="java.lang.String"/>
080: <form-property name="list" type="java.util.List"/>
081:
082: <!--
083: - properties for your object's field...
084: -->
085:
086: </form-bean>
087:
088: <action-mappings>
089:
090: <action
091: path ="/yourObject"
092: type ="com.methodhead.aikp.AikpAction"
093: parameter="com.your.Policy"
094: name ="yourObjectForm"
095: scope ="request"
096: input ="/yourObject.jsp"
097: validate ="true">
098: <forward name="list" path="/yourObjectList.jsp"/>
099: </action>
100:
101: </action-mappings>
102: </xmp>
103: <p>
104: In addition, the following message resources should be defined:
105: </p>
106: <xmp>
107: aikpaction.confirm=Delete {0}?
108: aikpaction.deleted=Deleted {0}.
109: aikpaction.saved=Saved {0}.
110: </xmp>
111: <p>
112: What operation is performed depends on the <tt>action</tt> parameter
113: (and sometimes the <tt>submit</tt> parameter). A minimal form looks
114: like:
115: </p>
116: <xmp>
117: <%@ page import="com.methodhead.aikp.AikpForm"%>
118:
119: <%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
120: <%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %>
121:
122:
123: <html:errors/>
124: <html:form action="/yourObject">
125: <html:hidden property="id"/>
126: <html:hidden property="action"/>
127:
128: <!--
129: - inputs for your object...
130: -->
131:
132: <input type="submit" name="submit" value="Submit"></input> <%
133:
134: if ( !form.get( "action" ).equals( "saveNew" ) ) { %>
135: <input type="submit" name="submit" value="Delete"></input> <%
136: } %>
137: </html:form>
138: </xmp>
139: <p>
140: A minimal list form looks like:
141: </p>
142: <xmp>
143: <%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html"%>
144: <%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean"%>
145: <%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic"%>
146:
147: <logic:iterate
148: name="yourObjectForm"
149: property="list"
150: id="yourObject"
151: type="com.your.Object">
152:
153: <a href="yourObject.do?action=edit&id=<%= yourObject.get( "id" ) %>"><%= yourObject.get( "yourField" ) %></a><br>
154:
155: </logic:iterate>
156: </xmp>
157: */
158: public abstract class AikpAction extends AuthAction {
159:
160: // constructors /////////////////////////////////////////////////////////////
161:
162: // constants ////////////////////////////////////////////////////////////////
163:
164: // classes //////////////////////////////////////////////////////////////////
165:
166: // methods //////////////////////////////////////////////////////////////////
167:
168: /**
169: * Returns a new instance of the persistable to be managed by this action.
170: */
171: protected abstract AutoIntKeyPersistable createPersistable(
172: OperationContext op);
173:
174: /**
175: * Returns the forward used when the persistable is saved; by default, a
176: * forward to input is returned.
177: */
178: protected ActionForward getForwardForSave(OperationContext op,
179: Object policy) {
180:
181: return new ActionForward(op.mapping.getInput());
182: }
183:
184: /**
185: * Returns the forward used when the persistable is deleted; by default, a
186: * the <tt>status</tt> forward is returned.
187: */
188: protected ActionForward getForwardForDelete(OperationContext op,
189: Object policy) {
190:
191: return op.mapping.findForward("status");
192: }
193:
194: /**
195: * Populates the specified form property with the corresponding value from
196: * <tt>persistable</tt> using it's <tt>toString()</tt> method (unless it's a
197: * date, in which case it is formatted with <tt>DateFormat.getDateInstance(
198: * DateFormat.SHORT )</tt>).
199: */
200: protected static void populateFormProperty(String name,
201: DynaActionForm form, AutoIntKeyPersistable persistable) {
202:
203: if (persistable.getDynaClass().getDynaProperty(name).getType() == Date.class) {
204: DateFormat dateFormat = DateFormat
205: .getDateInstance(DateFormat.SHORT);
206: form
207: .set(name, dateFormat.format(persistable
208: .getDate(name)));
209: } else {
210: form.set(name, persistable.get(name).toString());
211: }
212: }
213:
214: /**
215: * Populates the specified persistable property with the corresponding
216: * property from <tt>form</tt>, using {@link
217: * com.methodhead.persistable.Persistable#setAsObject
218: * Persistable.setAsObject()} to set the value.
219: */
220: protected static void populatePersistableField(String name,
221: AutoIntKeyPersistable persistable, DynaActionForm form) {
222:
223: persistable.setAsObject(name, form.get(name));
224: }
225:
226: /**
227: * Populates the <tt>form</tt> with <tt>persistable</tt>'s field values. All
228: * form fields are expected to be of type <tt>String</tt>.
229: */
230: protected void populateForm(DynaActionForm form,
231: AutoIntKeyPersistable persistable) {
232:
233: DynaProperty[] dynaProperties = persistable.getDynaClass()
234: .getDynaProperties();
235:
236: for (int i = 0; i < dynaProperties.length; i++) {
237: populateFormProperty(dynaProperties[i].getName(), form,
238: persistable);
239: }
240: }
241:
242: /**
243: * Populates the <tt>persistable</tt>'s field values with <tt>form</tt>. All
244: * form fields are expected to be of type <tt>String</tt>. Date fields are
245: * parsed using a <tt>DateFormat</tt> as returned by
246: * <tt>DateFormat.getDateTimeInstance()</tt>.
247: */
248: protected void populatePersistable(
249: AutoIntKeyPersistable persistable, DynaActionForm form) {
250:
251: DynaProperty[] dynaProperties = persistable.getDynaClass()
252: .getDynaProperties();
253:
254: for (int i = 0; i < dynaProperties.length; i++) {
255: populatePersistableField(dynaProperties[i].getName(),
256: persistable, form);
257: }
258: }
259:
260: /**
261: * Loads all elements from the database using {@link
262: * AutoIntKeyPersistable#loadAll loadAll()} and forwards to <tt>list</tt>.
263: */
264: protected ActionForward doList(OperationContext op, Object policy) {
265:
266: AutoIntKeyPersistable persistable = createPersistable(op);
267: op.form.set("list", persistable.loadAll(null, null));
268: return StrutsUtil.findForward(op.mapping, "list");
269: }
270:
271: /**
272: * Uses the persistable's default values to initialize the form; a forward to
273: * input is returned.
274: */
275: protected ActionForward doNew(OperationContext op, Object policy) {
276:
277: populateForm(op.form, createPersistable(op));
278: op.form.set("action", "saveNew");
279:
280: return new ActionForward(op.mapping.getInput());
281: }
282:
283: /**
284: * Loads the persistable using the form's <tt>id</tt> property and uses its
285: * field values to initialize the form; a forward to input is returned.
286: */
287: protected ActionForward doEdit(OperationContext op, Object policy) {
288:
289: AutoIntKeyPersistable persistable = createPersistable(op);
290: persistable.load(new IntKey(op.form.get("id")));
291: populateForm(op.form, persistable);
292: op.form.set("action", "save");
293:
294: return new ActionForward(op.mapping.getInput());
295: }
296:
297: /**
298: * <p>
299: * Called when an persistable is to be deleted, adds the
300: * <tt>aikpaction.confirm</tt> to the action's messages (accessible by the
301: * Struts <tt>html:messages</tt> tag) and returns a forward to
302: * <tt>confirm</tt>. <b>Note:</b> The persistable itself is argument 0 to
303: * the message, so make sure its <tt>toString()</tt> method returns
304: * something reasonable if you include <tt>{0}</tt> in your message.
305: * </p>
306: */
307: protected ActionForward doConfirm(OperationContext op, Object policy) {
308:
309: AutoIntKeyPersistable persistable = createPersistable(op);
310: persistable.load(new IntKey(op.form.get("id")));
311:
312: StrutsUtil.addMessage(op.request, "aikpaction.confirm",
313: persistable, null, null);
314:
315: op.form.set("action", "delete");
316:
317: return StrutsUtil.findForward(op.mapping, "confirm");
318: }
319:
320: /**
321: * Creates the persistable, sets its fields and calls its <tt>saveNew()</tt>
322: * method. The form is then repopulated with values from the saved
323: * persistable. The <tt>aikpaction.saved</tt> message is added to the action
324: * and a forward to input is returned.
325: */
326: protected ActionForward doSaveNew(OperationContext op, Object policy) {
327:
328: AutoIntKeyPersistable persistable = createPersistable(op);
329: populatePersistable(persistable, op.form);
330: persistable.saveNew();
331: populateForm(op.form, persistable);
332: StrutsUtil.addMessage(op.request, "aikpaction.saved",
333: persistable, null, null);
334: op.form.set("action", "save");
335:
336: return getForwardForSave(op, policy);
337: }
338:
339: /**
340: * Loads the persistable using the form's <tt>id</tt> property, sets its
341: * fields, and calls its <tt>save()</tt> method. The form is then
342: * repopulated with values from the saved persistable. The
343: * <tt>aikpaction.saved</tt> message is added to the action and a forward to
344: * input is returned.
345: */
346: protected ActionForward doSave(OperationContext op, Object policy) {
347:
348: AutoIntKeyPersistable persistable = createPersistable(op);
349: persistable.load(new IntKey(op.form.get("id")));
350: populatePersistable(persistable, op.form);
351: persistable.save();
352: populateForm(op.form, persistable);
353: StrutsUtil.addMessage(op.request, "aikpaction.saved",
354: persistable, null, null);
355: op.form.set("action", "save");
356:
357: return getForwardForSave(op, policy);
358: }
359:
360: /**
361: * Like <tt>doEdit()</tt>, <tt>doCancel()</tt> loads the persistable using
362: * the form's <tt>id</tt> property, populates the form, adds the
363: * <tt>aikpaction.cancelled</tt> message to the action, and returns a forward
364: * to input.
365: * @deprecated Use {@link #doCancelDelete}
366: */
367: protected ActionForward doCancel(OperationContext op, Object policy) {
368:
369: AutoIntKeyPersistable persistable = createPersistable(op);
370: persistable.load(new IntKey(op.form.get("id")));
371: populateForm(op.form, persistable);
372: op.form.set("action", "save");
373: StrutsUtil.addMessage(op.request, "aikpaction.cancelled",
374: persistable, null, null);
375:
376: return new ActionForward(op.mapping.getInput());
377: }
378:
379: /**
380: * Like <tt>doEdit()</tt>, <tt>doCancelDelete()</tt> loads the persistable using
381: * the form's <tt>id</tt> property, populates the form, adds the
382: * <tt>aikpaction.cancelled</tt> message to the action, and returns a forward
383: * to input.
384: */
385: protected ActionForward doCancelDelete(OperationContext op,
386: Object policy) {
387:
388: AutoIntKeyPersistable persistable = createPersistable(op);
389: persistable.load(new IntKey(op.form.get("id")));
390: populateForm(op.form, persistable);
391: op.form.set("action", "save");
392: StrutsUtil.addMessage(op.request, "aikpaction.cancelled",
393: persistable, null, null);
394:
395: return new ActionForward(op.mapping.getInput());
396: }
397:
398: /**
399: * Like <tt>doEdit()</tt>, <tt>doCancelSave()</tt> loads the persistable using
400: * the form's <tt>id</tt> property, populates the form, adds the
401: * <tt>aikpaction.cancelled</tt> message to the action, and returns a forward
402: * to input.
403: */
404: protected ActionForward doCancelSave(OperationContext op,
405: Object policy) {
406:
407: AutoIntKeyPersistable persistable = createPersistable(op);
408: persistable.load(new IntKey(op.form.get("id")));
409: populateForm(op.form, persistable);
410: op.form.set("action", "save");
411: StrutsUtil.addMessage(op.request, "aikpaction.cancelled",
412: persistable, null, null);
413:
414: return new ActionForward(op.mapping.getInput());
415: }
416:
417: /**
418: * Simply calls <tt>doList()</tt>
419: */
420: protected ActionForward doCancelSaveNew(OperationContext op,
421: Object policy) {
422:
423: return doList(op, policy);
424: }
425:
426: /**
427: * <p>
428: * Loads the persistable using the form's <tt>id</tt> property, and invokes
429: * its <tt>delete()</tt> method; a the <tt>status</tt> forward is returned.
430: * </p>
431: * <p>
432: * The <tt>aikpaction.deleted</tt> message is added to the action's messages
433: * (accessible by the Struts <tt>html:messages</tt> tag). <b>Note:</b> The
434: * persistable itself is argument 0 to the message, so make sure its
435: * <tt>toString()</tt> method returns something reasonable if you include
436: * <tt>{0}</tt> in your message.
437: * </p>
438: */
439: protected ActionForward doDelete(OperationContext op, Object policy) {
440:
441: AutoIntKeyPersistable persistable = createPersistable(op);
442: persistable.load(new IntKey(op.form.get("id")));
443: persistable.delete();
444:
445: StrutsUtil.addMessage(op.request, "aikpaction.deleted",
446: persistable, null, null);
447:
448: return getForwardForDelete(op, policy);
449: }
450:
451: /**
452: * Executes the action, calling the appropriate method (<tt>doNew()</tt>,
453: * <tt>doEdit()</tt>, etc.) according to the form's properties.
454: */
455: public ActionForward doExecute(ActionMapping mapping,
456: ActionForm form, HttpServletRequest request,
457: HttpServletResponse response) {
458:
459: DynaValidatorForm dynaForm = (DynaValidatorForm) form;
460: Object policy = StrutsUtil.getPolicy(mapping);
461: AuthUser user = AuthUtil.getUser(request);
462:
463: OperationContext op = new OperationContext(mapping, dynaForm,
464: request, response, user);
465:
466: String aikpaction = dynaForm.get("action").toString();
467:
468: if ("list".equals(aikpaction)) {
469: return doList(op, policy);
470: }
471:
472: if ("new".equals(aikpaction)) {
473: return doNew(op, policy);
474: }
475:
476: else if ("edit".equals(aikpaction)) {
477: return doEdit(op, policy);
478: }
479:
480: else if ("saveNew".equals(aikpaction)) {
481: if (!StringUtils.isBlank((String) dynaForm.get("cancel")))
482: return doCancelSaveNew(op, policy);
483: else
484: return doSaveNew(op, policy);
485: }
486:
487: else if ("save".equals(aikpaction)) {
488:
489: if (!StringUtils.isBlank((String) dynaForm.get("cancel")))
490: return doCancelSave(op, policy);
491: else if (!StringUtils.isBlank((String) dynaForm
492: .get("delete")))
493: return doConfirm(op, policy);
494: else
495: return doSave(op, policy);
496: }
497:
498: else if ("delete".equals(aikpaction)) {
499:
500: if (!StringUtils.isBlank((String) dynaForm.get("cancel")))
501: return doCancelDelete(op, policy);
502: else
503: return doDelete(op, policy);
504: }
505:
506: else {
507: throw new MhfException("Unexpected aikpaction \""
508: + aikpaction + "\"");
509: }
510: }
511:
512: // properties ///////////////////////////////////////////////////////////////
513:
514: // attributes ///////////////////////////////////////////////////////////////
515:
516: Logger logger_ = Logger.getLogger(AikpAction.class);
517: }
|