14.2.7 Realizing the new application components

Now that it is time to realize all the components that will support Edit mode in our portlet.

1. Create a global forward in struts-config.xml, that will be used by index.jsp.

a. In the Project Explorer view, expand Dynamic Web Projects | MyFirstStruts | Struts | /html/edit.

b. Double-click struts-config.xml.

c. Under the Global forwards page, click Add.

d. Name the global forward Welcome and enter /editConfiguration.do as the path.

e. Save the file and close editor.

2. Realize welcome.jsp.

a. In the Web diagram for /html/edit module, double-click welcome.jsp.
The New JSP file wizard appears. Note that the folder is correctly pointing to the html/edit folder under the Web application, since you inserted this information when you created the Web page in the Web diagram.

b. Click Finish.

c. Replace all code in the new jsp with the following code:

<%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic"%>

<logic:forward name="Welcome"/>

d. Save the file and close the editor.

3. Next, you will realize the userBean.

a. Double-click userBean form bean. This will bring the New Form-Bean wizard.

b. Leave the form bean name unchanged. Make sure that the Create an ActionForm class radio button is selected, and that Generic Form-bean Mapping is selected.

c. Click Next >.

d. Click Next > again.

e. In the Create new fields for your ActionForm class page, you are going to add a couple of fields.

ix. Click Add....

x. Enter the field's name username. Leave the field type as String.

xi. Click Add... again.

xii. Enter the field's name password and leave the field type as String.

xiii. The window will look like in Figure 14-14.

f. Click Next >.

Figure 14-14 Creating new fields for the ActionForm Bean

g. In the Creating a mapping for your ActionForm class, check only reset (which takes an HttpServletRequest as parameter and inherited abstract methods. Leave all other method stubs unchecked.

h. Click Finish.
Rational Developer will generate the ActionForm bean, that extends from rg.apache.struts.action.ActionForm, along with its proper accessor methods. It will also include the form-bean tag in struts-config.xml file.

i. Replace the reset method with the following code:

this.username = "";

this.password = "";

j. Save the file and close it.

4. Next, you will realize the editConfiguration action mapping.

a. In the Web diagram, double-click /editConfiguration.

b. The New action mapping wizard appears. As every fields are already properly set, click Finish.

c. Replace the execute method with the following code:

Example 14-5 The execute method from editConfiguration action mapping.

saveToken(request);

form.reset(mapping, request);

return (mapping.findForward("success"));

The code above initially calls the saveToken method, that Struts provides to avoid multiple submissions from one form. Next, the form bean is reset. This forces that the user always enter new values. Finally, the action forwards to success, that points to index.jsp.

d. Save the file and close it.

5. The next component you will realize is index.jsp.

a. In the Web diagram, double-click index.jsp.

b. Click Finish.

c. Replace the jsp contents with the code from Example 14-6:

Example 14-6 index.jsp for Edit mode

<%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %>

<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>

<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>

<h3><bean:message key="editmode.heading"/></h3>

<p><bean:message key="editmode.instructions"/></p>

<ul>

    <li><bean:message key="editmode.instructions.1"/></li>

    <li><bean:message key="editmode.instructions.2"/></li>

    <li><bean:message key="editmode.instructions.3"/></li>

    <%--<li><bean:message key="editmode.instructions.4"/></li>--%>

    <li><bean:message key="editmode.instructions.5"/></li>

</ul>

<ul>

  <html:messages message="true" id="error">

    <li><font color="red"><bean:write name="error"/></font></li>

  </html:messages>

</ul>  

<html:form action="/saveConfiguration.do" urlType="return" 
validate="false">

<table>

  <tr>

    <th align="right">

      <bean:message key="prompt.username"/>:&nbsp;

    </th>

    <td align="left">

      <html:text property="username" size="25"/>

    </td>

  </tr>

  <tr>

    <th align="right">

      <bean:message key="prompt.password"/>:&nbsp;

    </th>

    <td align="left">

      <html:password property="password" size="25"/>

    </td>

  </tr>

  <tr><td><br></td></tr>

  <tr>

    <td align="right">

      <html:submit value="Submit"/>

    </td>

    <td align="left">

      <html:reset/>

    </td>

  </tr>

</table>

</html:form>

Note that you added the html:messages tag, to support that messages from the action can be processed and displayed correctly.

Important: If you want execute an Action that will change the portlet mode to its previous mode, use the urlType attribute of the html:form tag with the value of return. This attribute was added by Struts portlet framework to the tags html:form, html:link and html:rewrite.

6. Finally, let's realize the saveConfiguration action.

a. In the Web diagram, double-click saveConfiguration action.

b. All values should be correct, as the Web diagram had all information the wizard needs to create the action.

c. Click Finish.

d. This action is the one with the most logic. Replace its contents with the code in Example 14-7 and save the file:

Example 14-7 Listing of SaveConfigurationAction.java

package myfirststruts.actions;
import myfirststruts.Constants;
import myfirststruts.forms.UserBean;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.jetspeed.portlet.PortletData;
import org.apache.jetspeed.portlet.PortletRequest;
import org.apache.jetspeed.portlet.Portlet.Mode;
import org.apache.jetspeed.portlet.Portlet.ModeModifier;
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 com.ibm.portal.struts.common.PortletApiUtils;
import com.ibm.wps.struts.action.StrutsAction;

public class SaveConfigurationAction extends StrutsAction {
 private Log log = LogFactory.getLog (this.getClass ());

 public ActionForward execute(ActionMapping mapping, ActionForm form,
   PortletRequest request) throws Exception {

  ActionForward forward = mapping.findForward("success");
  ActionMessages messages = new ActionMessages();

  if (form instanceof UserBean) {
   UserBean userBean = (UserBean) form;
   try {
    PortletApiUtils portletUtils =
       PortletApiUtils.getUtilsInstance();
    if (portletUtils != null) {
     PortletRequest portletRequest = 
       (PortletRequest) portletUtils
       .getPortletRequest(request);
     PortletData portletData = portletRequest.getData();

     if (portletRequest.getMode() == Mode.EDIT) {
      processEditData(request, messages, userBean,
        portletData);
     } else {
      log.debug("this command is only valid in edit mode");

      forward = null;
     }
     if (!messages.isEmpty()) {
      saveMessages(request, messages);

      portletRequest.setModeModifier(ModeModifier.CURRENT);

      forward = mapping.getInputForward();
     } else {
      if (portletData.getAttribute(Constants.USERNAME) 
        != null) {
       log.debug("Set view mode to user_set");
       ActionForward actionForward =
         mapping.findForward("user_set");
       if (actionForward != null) {
        portletUtils.createCommand(actionForward
          .getPath(), request, Mode.VIEW.toString());
       } else {
        log.debug("Could not find action forward for view mode");
       }
      } else {
       log.debug("Set view mode to user_not_set");
       ActionForward actionForward =
         mapping.findForward("user_not_set");
       if (actionForward != null) {
        portletUtils.createCommand(actionForward.
          getPath(), request, Mode.VIEW.toString());
       } else {
        log.debug("Could not find action forward for view mode");
       }
      }
     }
    }

   } catch (Exception ex) {
    log.debug("Could not save configuration " + ex.toString());
   }

  }
  return forward;
 }

 private void processEditData(PortletRequest request, ActionMessages messages, UserBean userBean, PortletData portletData) throws Exception {
  if (!isTokenValid(request)) {
   messages.add(ActionMessages.GLOBAL_MESSAGE,
       new ActionMessage("error.transaction.token"));
   log.debug("token is not valid");
  } else {
   log.debug("token is valid");
   // token was valid.
   String username = userBean.getUsername();
 
   if (username != null && !username.equals("")) {
    if (username.equals("error")) {
     messages.add(ActionMessages.GLOBAL_MESSAGE,
       new ActionMessage("error.invalid.username"));

    } else {
     portletData.setAttribute(Constants.USERNAME,
       userBean.getUsername());
    }
   } else {
    portletData.removeAttribute(Constants.USERNAME);
   }

   if (userBean.getPassword() != null &&
     !userBean.getPassword().equals("")) {
    portletData.setAttribute(Constants.PASSWORD,
      userBean.getPassword());
   } else {
    portletData.removeAttribute(Constants.PASSWORD);
   }
   portletData.store();
  }
  resetToken(request);
 }
}

Let's take a look at the major points of this code:

First of all, you had to retrieve the PortletApiUtils instance. It is the PortletApiUtils that gives us access to the PortletRequest object.

Following, you check if the portlet is in Edit mode, with the following code:

if (portletRequest.getMode() == Mode.EDIT)

This way, you guarantee that the action will work only and if only the portlet mode is Edit.

Next, the processEditData method is executed. First of all, it checks if the token is valid, with

if (!isTokenValid(request))

This is to avoid multiple submits of the form.

If the username and password attributes from UserBean are null, the code removes these attributes from PortletData. If not, the code stores them in PortletData, using the setAttribute method. The only exception is when the enter error as the username. In this case, a message is generated, stating that this is an invalid user name. The processEditData also stores the PortletData object with its new attribute values, and reset the token.

Back to the execute method, the code checks if there is any message to be shown. If there is any message, then it obligates the portlet to stay at the same mode, with

portletRequest.setModeModifier(ModeModifier.CURRENT);

and forwards to the page that invoked this action, with

forward = mapping.getInputForward();

Finally, the code tests if the username attribute was stored at PortletData, if yes, than it creates a command that makes the View mode page change to the one specified by the user_set local forward.

Example 14-8 The username attribute was found. Forwarding to user_set

ActionForward actionForward = mapping.findForward("user_set");
if (actionForward != null) {
 portletUtils.createCommand(actionForward.getPath(), request,
   Mode.VIEW.toString());
} else {
 log.debug("Could not find action forward for view mode");
}

If the username attribute is not stored at PortletData, the View mode forward is to the user_not_set local forward:

Example 14-9 The username attribute was not found. Forwarding to user_not_set

ActionForward actionForward = mapping.findForward("user_not_set");
if (actionForward != null) {
 portletUtils.createCommand(actionForward.getPath(), request,
   Mode.VIEW.toString());
} else {
 log.debug("Could not find action forward for view mode");
}

7. Add new keys to resources file:

a. Edit myfirststruts.resources.ApplicationResources.properties.

b. Add the following keys at the end of the file:

Example 14-10 The new messages to be included in the resources file

editmode.heading=Edit Mode
editmode.title=Struts Edit Mode Example

editmode.instructions=Try the options below to demonstrate various Struts features. When a valid username and password are given, an action forwards to a JSP in view mode. Valid usernames and passwords are between 3 and 16 characters long.
editmode.instructions.1=Click the <b>Submit</b> button without a valid username and/or password to see dynamic validation
editmode.instructions.2=Enter a username of "error" and a valid password to see ActionErrors supported
editmode.instructions.3=Enter a username of "delete" and a valid password to remove the configuration
editmode.instructions.4=Refresh the page to demonstrate transaction support
editmode.instructions.5=Enter a valid username and password to complete the configuration and change the mode to view mode

prompt.username=Username
prompt.password=Password

button.back=back
button.send=Send
button.save=Save
button.cancel=Cancel
button.reset=Reset

error.transaction.token=Transaction token is not valid
error.invalid.username=The username is not valid

c. Save the file and close editor.

8. Run the code:

a. Right-click the MyFirstStruts project.

b. Select Run | Run on Server.

c. After the welcome page displays, click the edit icon, go to Edit mode.

d. Try the code. You can choose any of the following:

If you leave the username blank, or if you enter delete, the username and password attributes will be removed from PortletData, and you will be forwarded to notConfigured.jsp.

If you enter error as the username, the application shows an error message and continues in Edit mode.

If you enter any other value to username and password, the application stores these values at PortletData and forwards to configured.jsp.

Figure 14-15 The portlet in Edit mode


Redbooks
ibm.com/redbooks