/*
* Copyright (C) 2006 Methodhead Software LLC. All rights reserved.
*
* This file is part of TransferCM.
*
* TransferCM is free software; you can redistribute it and/or modify it under the
* terms of the GNU General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option) any later
* version.
*
* TransferCM is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* TransferCM; if not, write to the Free Software Foundation, Inc., 51 Franklin St,
* Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.methodhead.aikp;
import java.text.DateFormat;
import java.text.ParseException;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.methodhead.auth.AuthAction;
import com.methodhead.util.StrutsUtil;
import com.methodhead.persistable.PersistableException;
import com.methodhead.MhfException;
import org.apache.log4j.Logger;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.ActionMessage;
import org.apache.struts.action.ActionMessages;
import org.apache.struts.validator.DynaValidatorForm;
import org.apache.struts.action.DynaActionForm;
import org.apache.commons.beanutils.DynaProperty;
import com.methodhead.util.StrutsUtil;
import com.methodhead.util.OperationContext;
import com.methodhead.auth.AuthUser;
import com.methodhead.auth.AuthUtil;
import org.apache.commons.lang.StringUtils;
/**
<p>
An Struts action with which to build a web interface for {@link
com.methodhead.aikp.AutoIntKeyPersistable
AutoIntKeyPersistable}s. This action will perform the following
operations: new, edit, save, and delete. Methods for these operations
and a number of support methods can be overloaded to achieve more
sophisticated behaviour.
</p>
<p>
Using this action requires at least this Struts configuration:
</p>
<xmp>
<form-bean
name ="yourObjectForm"
dynamic="true"
type ="com.methodhead.aikp.AikpForm">
<form-property name="action" type="java.lang.String"/>
<form-property name="id" type="java.lang.String"/>
<form-property name="submit" type="java.lang.String"/>
<form-property name="cancel" type="java.lang.String"/>
<form-property name="delete" type="java.lang.String"/>
<form-property name="list" type="java.util.List"/>
<!--
- properties for your object's field...
-->
</form-bean>
<action-mappings>
<action
path ="/yourObject"
type ="com.methodhead.aikp.AikpAction"
parameter="com.your.Policy"
name ="yourObjectForm"
scope ="request"
input ="/yourObject.jsp"
validate ="true">
<forward name="list" path="/yourObjectList.jsp"/>
</action>
</action-mappings>
</xmp>
<p>
In addition, the following message resources should be defined:
</p>
<xmp>
aikpaction.confirm=Delete {0}?
aikpaction.deleted=Deleted {0}.
aikpaction.saved=Saved {0}.
</xmp>
<p>
What operation is performed depends on the <tt>action</tt> parameter
(and sometimes the <tt>submit</tt> parameter). A minimal form looks
like:
</p>
<xmp>
<%@ page import="com.methodhead.aikp.AikpForm"%>
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %>
<html:errors/>
<html:form action="/yourObject">
<html:hidden property="id"/>
<html:hidden property="action"/>
<!--
- inputs for your object...
-->
<input type="submit" name="submit" value="Submit"></input> <%
if ( !form.get( "action" ).equals( "saveNew" ) ) { %>
<input type="submit" name="submit" value="Delete"></input> <%
} %>
</html:form>
</xmp>
<p>
A minimal list form looks like:
</p>
<xmp>
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html"%>
<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean"%>
<%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic"%>
<logic:iterate
name="yourObjectForm"
property="list"
id="yourObject"
type="com.your.Object">
<a href="yourObject.do?action=edit&id=<%= yourObject.get( "id" ) %>"><%= yourObject.get( "yourField" ) %></a><br>
</logic:iterate>
</xmp>
*/
public abstract class AikpAction extends AuthAction {
// constructors /////////////////////////////////////////////////////////////
// constants ////////////////////////////////////////////////////////////////
// classes //////////////////////////////////////////////////////////////////
// methods //////////////////////////////////////////////////////////////////
/**
* Returns a new instance of the persistable to be managed by this action.
*/
protected abstract AutoIntKeyPersistable createPersistable(
OperationContext op );
/**
* Returns the forward used when the persistable is saved; by default, a
* forward to input is returned.
*/
protected ActionForward getForwardForSave(
OperationContext op,
Object policy ) {
return new ActionForward( op.mapping.getInput() );
}
/**
* Returns the forward used when the persistable is deleted; by default, a
* the <tt>status</tt> forward is returned.
*/
protected ActionForward getForwardForDelete(
OperationContext op,
Object policy ) {
return op.mapping.findForward( "status" );
}
/**
* Populates the specified form property with the corresponding value from
* <tt>persistable</tt> using it's <tt>toString()</tt> method (unless it's a
* date, in which case it is formatted with <tt>DateFormat.getDateInstance(
* DateFormat.SHORT )</tt>).
*/
protected static void populateFormProperty(
String name,
DynaActionForm form,
AutoIntKeyPersistable persistable ) {
if ( persistable.getDynaClass().getDynaProperty( name ).getType() ==
Date.class ) {
DateFormat dateFormat = DateFormat.getDateInstance( DateFormat.SHORT );
form.set( name, dateFormat.format( persistable.getDate( name ) ) );
}
else {
form.set( name, persistable.get( name ).toString() );
}
}
/**
* Populates the specified persistable property with the corresponding
* property from <tt>form</tt>, using {@link
* com.methodhead.persistable.Persistable#setAsObject
* Persistable.setAsObject()} to set the value.
*/
protected static void populatePersistableField(
String name,
AutoIntKeyPersistable persistable,
DynaActionForm form ) {
persistable.setAsObject( name, form.get( name ) );
}
/**
* Populates the <tt>form</tt> with <tt>persistable</tt>'s field values. All
* form fields are expected to be of type <tt>String</tt>.
*/
protected void populateForm(
DynaActionForm form,
AutoIntKeyPersistable persistable ) {
DynaProperty[] dynaProperties =
persistable.getDynaClass().getDynaProperties();
for ( int i = 0; i < dynaProperties.length; i++ ) {
populateFormProperty( dynaProperties[ i ].getName(), form, persistable );
}
}
/**
* Populates the <tt>persistable</tt>'s field values with <tt>form</tt>. All
* form fields are expected to be of type <tt>String</tt>. Date fields are
* parsed using a <tt>DateFormat</tt> as returned by
* <tt>DateFormat.getDateTimeInstance()</tt>.
*/
protected void populatePersistable(
AutoIntKeyPersistable persistable,
DynaActionForm form ) {
DynaProperty[] dynaProperties =
persistable.getDynaClass().getDynaProperties();
for ( int i = 0; i < dynaProperties.length; i++ ) {
populatePersistableField(
dynaProperties[ i ].getName(), persistable, form );
}
}
/**
* Loads all elements from the database using {@link
* AutoIntKeyPersistable#loadAll loadAll()} and forwards to <tt>list</tt>.
*/
protected ActionForward doList(
OperationContext op,
Object policy ) {
AutoIntKeyPersistable persistable = createPersistable( op );
op.form.set(
"list", persistable.loadAll( null, null ) );
return StrutsUtil.findForward( op.mapping, "list" );
}
/**
* Uses the persistable's default values to initialize the form; a forward to
* input is returned.
*/
protected ActionForward doNew(
OperationContext op,
Object policy ) {
populateForm( op.form, createPersistable( op ) );
op.form.set( "action", "saveNew" );
return new ActionForward( op.mapping.getInput() );
}
/**
* Loads the persistable using the form's <tt>id</tt> property and uses its
* field values to initialize the form; a forward to input is returned.
*/
protected ActionForward doEdit(
OperationContext op,
Object policy ) {
AutoIntKeyPersistable persistable = createPersistable( op );
persistable.load( new IntKey( op.form.get( "id" ) ) );
populateForm( op.form, persistable );
op.form.set( "action", "save" );
return new ActionForward( op.mapping.getInput() );
}
/**
* <p>
* Called when an persistable is to be deleted, adds the
* <tt>aikpaction.confirm</tt> to the action's messages (accessible by the
* Struts <tt>html:messages</tt> tag) and returns a forward to
* <tt>confirm</tt>. <b>Note:</b> The persistable itself is argument 0 to
* the message, so make sure its <tt>toString()</tt> method returns
* something reasonable if you include <tt>{0}</tt> in your message.
* </p>
*/
protected ActionForward doConfirm(
OperationContext op,
Object policy ) {
AutoIntKeyPersistable persistable = createPersistable( op );
persistable.load( new IntKey( op.form.get( "id" ) ) );
StrutsUtil.addMessage(
op.request, "aikpaction.confirm", persistable, null, null );
op.form.set( "action", "delete" );
return StrutsUtil.findForward( op.mapping, "confirm" );
}
/**
* Creates the persistable, sets its fields and calls its <tt>saveNew()</tt>
* method. The form is then repopulated with values from the saved
* persistable. The <tt>aikpaction.saved</tt> message is added to the action
* and a forward to input is returned.
*/
protected ActionForward doSaveNew(
OperationContext op,
Object policy ) {
AutoIntKeyPersistable persistable = createPersistable( op );
populatePersistable( persistable, op.form );
persistable.saveNew();
populateForm( op.form, persistable );
StrutsUtil.addMessage(
op.request, "aikpaction.saved", persistable, null, null );
op.form.set( "action", "save" );
return getForwardForSave( op, policy );
}
/**
* Loads the persistable using the form's <tt>id</tt> property, sets its
* fields, and calls its <tt>save()</tt> method. The form is then
* repopulated with values from the saved persistable. The
* <tt>aikpaction.saved</tt> message is added to the action and a forward to
* input is returned.
*/
protected ActionForward doSave(
OperationContext op,
Object policy ) {
AutoIntKeyPersistable persistable = createPersistable( op );
persistable.load( new IntKey( op.form.get( "id" ) ) );
populatePersistable( persistable, op.form );
persistable.save();
populateForm( op.form, persistable );
StrutsUtil.addMessage(
op.request, "aikpaction.saved", persistable, null, null );
op.form.set( "action", "save" );
return getForwardForSave( op, policy );
}
/**
* Like <tt>doEdit()</tt>, <tt>doCancel()</tt> loads the persistable using
* the form's <tt>id</tt> property, populates the form, adds the
* <tt>aikpaction.cancelled</tt> message to the action, and returns a forward
* to input.
* @deprecated Use {@link #doCancelDelete}
*/
protected ActionForward doCancel(
OperationContext op,
Object policy ) {
AutoIntKeyPersistable persistable = createPersistable( op );
persistable.load( new IntKey( op.form.get( "id" ) ) );
populateForm( op.form, persistable );
op.form.set( "action", "save" );
StrutsUtil.addMessage(
op.request, "aikpaction.cancelled", persistable, null, null );
return new ActionForward( op.mapping.getInput() );
}
/**
* Like <tt>doEdit()</tt>, <tt>doCancelDelete()</tt> loads the persistable using
* the form's <tt>id</tt> property, populates the form, adds the
* <tt>aikpaction.cancelled</tt> message to the action, and returns a forward
* to input.
*/
protected ActionForward doCancelDelete(
OperationContext op,
Object policy ) {
AutoIntKeyPersistable persistable = createPersistable( op );
persistable.load( new IntKey( op.form.get( "id" ) ) );
populateForm( op.form, persistable );
op.form.set( "action", "save" );
StrutsUtil.addMessage(
op.request, "aikpaction.cancelled", persistable, null, null );
return new ActionForward( op.mapping.getInput() );
}
/**
* Like <tt>doEdit()</tt>, <tt>doCancelSave()</tt> loads the persistable using
* the form's <tt>id</tt> property, populates the form, adds the
* <tt>aikpaction.cancelled</tt> message to the action, and returns a forward
* to input.
*/
protected ActionForward doCancelSave(
OperationContext op,
Object policy ) {
AutoIntKeyPersistable persistable = createPersistable( op );
persistable.load( new IntKey( op.form.get( "id" ) ) );
populateForm( op.form, persistable );
op.form.set( "action", "save" );
StrutsUtil.addMessage(
op.request, "aikpaction.cancelled", persistable, null, null );
return new ActionForward( op.mapping.getInput() );
}
/**
* Simply calls <tt>doList()</tt>
*/
protected ActionForward doCancelSaveNew(
OperationContext op,
Object policy ) {
return doList( op, policy );
}
/**
* <p>
* Loads the persistable using the form's <tt>id</tt> property, and invokes
* its <tt>delete()</tt> method; a the <tt>status</tt> forward is returned.
* </p>
* <p>
* The <tt>aikpaction.deleted</tt> message is added to the action's messages
* (accessible by the Struts <tt>html:messages</tt> tag). <b>Note:</b> The
* persistable itself is argument 0 to the message, so make sure its
* <tt>toString()</tt> method returns something reasonable if you include
* <tt>{0}</tt> in your message.
* </p>
*/
protected ActionForward doDelete(
OperationContext op,
Object policy ) {
AutoIntKeyPersistable persistable = createPersistable( op );
persistable.load( new IntKey( op.form.get( "id" ) ) );
persistable.delete();
StrutsUtil.addMessage(
op.request, "aikpaction.deleted", persistable, null, null );
return getForwardForDelete( op, policy );
}
/**
* Executes the action, calling the appropriate method (<tt>doNew()</tt>,
* <tt>doEdit()</tt>, etc.) according to the form's properties.
*/
public ActionForward doExecute(
ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response ) {
DynaValidatorForm dynaForm = ( DynaValidatorForm )form;
Object policy = StrutsUtil.getPolicy( mapping );
AuthUser user = AuthUtil.getUser( request );
OperationContext op =
new OperationContext( mapping, dynaForm, request, response, user );
String aikpaction = dynaForm.get( "action" ).toString();
if ( "list".equals( aikpaction ) ) {
return doList( op, policy );
}
if ( "new".equals( aikpaction ) ) {
return doNew( op, policy );
}
else if ( "edit".equals( aikpaction ) ) {
return doEdit( op, policy );
}
else if ( "saveNew".equals( aikpaction ) ) {
if ( !StringUtils.isBlank( ( String )dynaForm.get( "cancel" ) ) )
return doCancelSaveNew( op, policy );
else
return doSaveNew( op, policy );
}
else if ( "save".equals( aikpaction ) ) {
if ( !StringUtils.isBlank( ( String )dynaForm.get( "cancel" ) ) )
return doCancelSave( op, policy );
else if ( !StringUtils.isBlank( ( String )dynaForm.get( "delete" ) ) )
return doConfirm( op, policy );
else
return doSave( op, policy );
}
else if ( "delete".equals( aikpaction ) ) {
if ( !StringUtils.isBlank( ( String )dynaForm.get( "cancel" ) ) )
return doCancelDelete( op, policy );
else
return doDelete( op, policy );
}
else {
throw new MhfException( "Unexpected aikpaction \"" + aikpaction + "\"" );
}
}
// properties ///////////////////////////////////////////////////////////////
// attributes ///////////////////////////////////////////////////////////////
Logger logger_ = Logger.getLogger( AikpAction.class );
}
|