Wednesday, May 4, 2011

Dealing with a tricky ognl.MethodFailedException

I'm trying to reuse an existing JSP form for both creating and editing an entity.  In my case, it's an entity named Tag (not to be confused with JSP tags).  I had everything in working order with Struts 2.2.1.1 for creating my entities and was just beginning the process of integrating edit / update capability when I encountered the tricky error (actually a warning) described below.

The first thing I did in an attempt to add edit capability was add a hidden "id" field to my JSP form so I can keep track of which entity I'm updating.  Since I'm reusing the same form for both creating and editing my entity, the "id" will be submitted without a value on each post for creating a new Tag. Here's what I added to my JSP:
<s:hidden name="id" />
After making this small change I did a function test to make sure I could still create new Tags and saw the following stack trace in the log as excerpted below:
com.opensymphony.xwork2.ognl.OgnlValueStack - Error setting expression 'id' with value '[Ljava.lang.String;@100ac03'
ognl.MethodFailedException: Method "setId" failed for object org.robbins.flashcards.model.Tag@1b9eb34 [name='null' ]
[java.lang.NoSuchMethodException: org.robbins.flashcards.model.Tag.setId([Ljava.lang.String;)] at ognl.OgnlRuntime.callAppropriateMethod(OgnlRuntime.java:1285)
It's complaining because ognl is not finding a setId() method on my POJO that takes a String value.  Well, since "id" is an integer, of course my POJO won't accept a String.  Remember, I'm getting this error when trying to create a new Tag and the hidden "id" field is going to be empty at this point.  It gets submitted as empty, I mean literally an empty string ("").  Ognl it trying to assign a string to an integer field and is complaining when it doesn't find an appropriate method on my POJO.  Maybe this is by design.

I wasn't sure how to deal with this issue.  As usual, I did some Googling but this time I didn't find a suitable solution to my problem.  Next, I posted to StackOverflow and the Struts User distribution list.

A response on StackOverflow suggested it might be an old bug in ognl and to try using a newer version of the ognl jar.  I was already using ognl-3.0.jar which is included in the Struts 2.2.1.1 distribution.  In any case, I replaced it with ognl-3.0.1.jar which I obtained from the Maven2 repository and continued receiving the same error.

My post to Struts User didn't garner any traction at first so I decided to try some more Googling.  I found something that sparked a light bulb in my head.  I found a blog post in Chinese which, although I couldn't read the explanation, appeared to suggest modifying the logging level for ognl packages from Warn to Error.

Could it be that simple?  After all, the stack trace in the log was a Warn and not an Error.  Maybe I could just ignore it.

On further inspection, my create code was actually completing successfully even with the stack trace.  I then finished modifying my JSP, Action, and business tier code to enable editing of existing Tag entities.  Lo and behold, when submitting the edit form, which by the way has a value in the hidden "id" form tag, the "id" is successfully set on my POJO.  Ognl was only throwing the stack trace when the "id" field had no value.

The post to the Struts User List later received some responses and one of the folks suggested to "default the value or make sure you supply a value for your hidden field and base your application logic on this value".

In the end I just modified my log4j.properties as follows:
# Struts OgnlUtil issues unimportant warnings
log4j.logger.com.opensymphony.xwork2.util.OgnlUtil=error
log4j.logger.com.opensymphony.xwork2.ognl.OgnlValueStack=error 

 What do you think?  Prefer a different approach?  Please feel free to comment.

Monday, May 2, 2011

Creating a Struts 2 "Welcome" page

Creating a "welcome" page in a Java web application is usually a simple matter.  Just modify the web.xml with your <welcome-file> and away you go.  However, it's not so simple to have my <welcome-file> point to a Struts 2 Action.  It seems the web container is expecting a JSP or HTML file in the <welcome-file> tag.

I did some Googling and read some posts on stackoverflow and theserverside on this same issue.  There were several suggested approaches.  The two most common suggestions were:
  1. Create a index.jsp and issue a <% response.sendRedirect(myaction.do); %> or alternatively a <jsp:forward page="/myaction.do" />.  Of course you'll also need to add index.jsp as a <welcome-file> in your web.xml.
  2. Create an empty file named welcome.do.  Add welcome.do to your <welcome-file> tag and create a Struts Action mapping for welcome.do.  This is a nice little hack and Struts will go ahead and serve your Action.
I decided to use the second suggestion in my Struts 2 web application.  Here's the steps I used.
  1. Create an empty file named "welcome.action" in "FlashCardsWeb\WebContent"
  2. Modified the web.xml as follows:
    <welcome-file-list>
        <welcome-file>welcome.action</welcome-file>
    </welcome-file-list>
  3. Modified the struts.xml to create the following Action.  "baseLayout" is Tiles definition that includes my header, menu, body, and footer.
    <action name="welcome">
        <result name="success" type="tiles">baseLayout</result>
    </action>
I'm sure there are other approaches.  What are you using?  Feel free to comment with your comments or solution.  Thanks!