Friday, February 21, 2014

"Portlet Programming" Errata

There is a coding error in Chapter 4 of Cameron McKenzie's "Portlet Programming" book.  I only have the first edition of the book so I don't know if it has already been identified and fixed or not.

In the book he tells us that to create a link back to the page that the portal is on we should use RenderResponse's createRenderURL( ) method.  He demonstrates how to use this in the NumberGuesserPortlet class where in the doView method we create a link back to the page so that the user can guess the number again.
printWriter.println("<a href = \"" + response.createRenderURL() + "\">Try Again</a>");
Unfortunately he uses the same method in the form's action attribute.
<form action = "<%= renderResponse.createRenderURL( ) %>" method = "post">
This will not send the form parameters to the NumberGuesserPortlet.  This is what it should be instead.
<form action = "<%= renderResponse.createActionURL( ) %>" method = "post">
You'll also need to add the following to the NumberGuesserPortlet class.
@Override
public void processAction(ActionRequest actionRequest, ActionResponse actionResponse)
   throws PortletException, IOException {
  
   String parameterName = actionResponse.getNamespace() + "number";
   String parameterValue = actionRequest.getParameter(parameterName);

   actionResponse.setRenderParameter(parameterName, parameterValue);
}
Here's how you would pass parameters using renderURL:
<portlet:renderURL var = "link" >
   <portlet:param name = "number" value = "1234" />
</portlet:renderURL>

<a href = "<%= link >">
   Click Me
</a>

I came across another problem when I tried to run the StateShifterPortlet example from Chapter 6. Specifically, in the processAction method, Cameron uses the equality operator (ie. ==) to check the status of the portlet window.
// taken from page 116 of Cameron McKenzie's "Portlet Programming"
@Override
public void processAction(ActionRequest actionRequest, ActionResponse actionResponse)
   throws PortletException, IOException {

   if (actionRequest.getParameter("shift") != null) {
      WindowState state = actionRequest.getWindowState();

      if (state == WindowState.NORMAL) {
         actionResponse.setWindowState(WindowState.MAXIMIZED);
      }

      if (state == WindowState.MAXIMIZED) {
         actionResponse.setWindowState(WindowState.MINIMIZED);
      }

      if (state == WindowState.MINIMIZED) {
         actionResponse.setWindowState(WindowState.NORMAL);
      }
   }
}
I'm still not entirely sure why this doesn't work because WindowState objects are immutable; you can't change their internal state. Therefore, you would think that the implementation of ActionResponse's setWindowState would just be something like the following as you wouldn't need to make a defensive copy.
public void setWindowState(WindowState windowState) {
   this.windowState = windowState;
}
See Also: Java Practices' article on "Defensive Copying"

Consider what would happen if you did the following:
actionResponse.setWindowState(new WindowState("normal"));
Obviously ActionRequest's windowState field wouldn't be pointing to the same object reference as WindowState.NORMAL, but the two objects would still be equal to each other.  According to the JavaDocs for WindowState's equals method, two WindowState's are equal "...if the String's equals method for the String representing the two window states returns true."

Anyway to get the StateShifterPortlet example to work I had to use the equals method instead of the equality operator like so:
@Override
public void processAction(ActionRequest actionRequest, ActionResponse actionResponse)
   throws PortletException, IOException {

   if (actionRequest.getParameter("shift") != null) {
      WindowState state = actionRequest.getWindowState();

      if (state.equals(WindowState.NORMAL)) {
         actionResponse.setWindowState(WindowState.MAXIMIZED);
      }

      if (state.equals(WindowState.MAXIMIZED)) {
         actionResponse.setWindowState(WindowState.MINIMIZED);
      }

      if (state.equals(WindowState.MINIMIZED)) {
         actionResponse.setWindowState(WindowState.NORMAL);
      }
   }
}
See Also: ProgrammerInterview.com's article "What's the difference between equals() and ==?"

No comments:

Post a Comment