Unexpected Exception caught setting 'xxx' on 'class xxx: Error setting expression 'xxx' with value ['x', ]

Tiny picture Tiny · Jan 9, 2014 · Viewed 23.4k times · Source

I'm passing some parameters to an action class implementing ModelDriven<Transporter> through a query-string.

<s:form namespace="/admin_side" action="Test" id="dataForm" name="dataForm">
    <s:url id="editURL" action="EditTest" escapeAmp="false">
        <s:param name="transporterId" value="1"/>
        <s:param name="transporterName" value="'DHL'"/>
    </s:url>
    <s:a href="%{editURL}">Click</s:a>
</s:form>

The action class is as follows.

@Namespace("/admin_side")
@ResultPath("/WEB-INF/content")
@ParentPackage(value = "struts-default")
public final class TestAction extends ActionSupport 
                           implements Serializable, Preparable, ModelDriven<Transporter>
{
    private static final long serialVersionUID = 1L;
    private Transporter transporter = new Transporter();

    @Action(value = "Test",
    results = {
        @Result(name = ActionSupport.SUCCESS, location = "Test.jsp"),
        @Result(name = ActionSupport.INPUT, location = "Test.jsp")},
    interceptorRefs = {
        @InterceptorRef(value = "paramsPrepareParamsStack", 
                 params = {"params.acceptParamNames", "transporterId, transporterName"})})
    public String load() throws Exception {
        return ActionSupport.SUCCESS;
    }

    @Action(value = "EditTest",
    results = {
        @Result(name = ActionSupport.SUCCESS, location = "Test.jsp"),
        @Result(name = ActionSupport.INPUT, location = "Test.jsp")},
    interceptorRefs = {
        @InterceptorRef(value = "paramsPrepareParamsStack", 
                 params = {"params.acceptParamNames", "transporterId, transporterName"})})
    public String edit() {
        System.out.println(transporter.getTransporterId() 
                         + " : " + transporter.getTransporterName());
        return ActionSupport.SUCCESS;
    }

    @Override
    public Transporter getModel() {
        return transporter;
    }

    @Override
    public void prepare() throws Exception {}
}

The server terminal displays the following messages.

Jan 09, 2014 4:06:32 PM com.opensymphony.xwork2.interceptor.ParametersInterceptor error
SEVERE: Developer Notification (set struts.devMode to false to disable this message):
Unexpected Exception caught setting 'transporterId' on 'class actions.TestAction: Error setting expression 'transporterId' with value ['1', ]
Jan 09, 2014 4:06:32 PM com.opensymphony.xwork2.interceptor.ParametersInterceptor error
SEVERE: Developer Notification (set struts.devMode to false to disable this message):
Unexpected Exception caught setting 'transporterName' on 'class actions.TestAction: Error setting expression 'transporterName' with value ['DHL', ]

Even though the log level is SEVERE, the values of these parameters are available in the action class as

System.out.println(transporter.getTransporterId() 
                 + " : " + transporter.getTransporterName());

in the edit() method.

If paramsPrepareParamsStack is replaced by defaultStack then, these messages disappear.

Expressions like ['DHL', ] indicate an array. transporterId and transporterName in the model are, however of type Long and String respectively.

What am I doing wrong?

Answer

Andrea Ligios picture Andrea Ligios · Jan 9, 2014

No array problem is involved here (even if it seems like that): this kind of exception means that Struts can't find a Setter for your parameter:

From ParametersInterceptor documentation:

Warning on missing parameters

When there is no setter for given parameter name, a warning message like below will be logged in devMode:

SEVERE: Developer Notification (set struts.devMode to false to disable this 
message):
Unexpected Exception caught setting 'search' on 'class demo.ItemSearchAction: 
Error setting expression 'search' with value ['search', ]
Error setting expression 'search' with value ['search', ] - [unknown location] 
  at com.opensymphony.xwork2.ognl.OgnlValueStack.handleRuntimeException(OgnlValueStack.java:201)
  at com.opensymphony.xwork2.ognl.OgnlValueStack.setValue(OgnlValueStack.java:178)
  at com.opensymphony.xwork2.ognl.OgnlValueStack.setParameter(OgnlValueStack.java:152)

Thus is expected behaviour to allow developer to spot missing setter or typo in either parameter name or setter.

You can easily reproduce this error by putting an element in JSP that does not exist in the Action.

Since your properties exist (with their Setters) in the Model, and you are using ModelDriven and paramsPrepareParamsStack, what I think is going on is:

  • ModelDriven Interceptor is delegated to handle the Model object;
  • The first time you call Parameters Interceptor, ModelDriven Interceptor has not run yet;
  • Then your Action knows nothing about the Model object, and try to find the Setters for your parameters in the Action, NOT in the Model.
  • The second interceptor instead runs after the ModelDriven one, and knows exactly where to set the parameters. This is why you have the parameters correctly set in the Action method.

But if this is true, then you should NOT be able to retrieve those parameters in the prepare() method (that is the reason you are using this stack...):
please try, and post here the result.

The first thing that comes to my mind to resolve this issue, is to place the ModelDriven Interceptor before the first Parameters Interceptor (by either copying it, or by moving it, I'm not sure which kind of side effect, if any, it could produce in both cases, you should again try and reporting it here).

Then define the following stack, and use it.

<interceptor-stack name="modelParamsPrepareParamsStack">
    <interceptor-ref name="exception"/>
    <interceptor-ref name="alias"/>
    <interceptor-ref name="i18n"/>
    <interceptor-ref name="checkbox"/>
    <interceptor-ref name="multiselect"/>

    <!-- NEW ModelDriven Position -->
    <interceptor-ref name="modelDriven"/>

    <interceptor-ref name="params">
        <param name="excludeParams">^dojo\..*,^struts\..*,^session\..*,^request\..*,^application\..*,^servlet(Request|Response)\..*,^parameters\..*,^action:.*,^method:.*</param>
    </interceptor-ref>
    <interceptor-ref name="servletConfig"/>
    <interceptor-ref name="prepare"/>
    <interceptor-ref name="chain"/>

    <!-- OLD ModelDriven Position -->
    <!--interceptor-ref name="modelDriven"/-->

    <interceptor-ref name="fileUpload"/>
    <interceptor-ref name="staticParams"/>
    <interceptor-ref name="actionMappingParams"/>
    <interceptor-ref name="params">
        <param name="excludeParams">^dojo\..*,^struts\..*,^session\..*,^request\..*,^application\..*,^servlet(Request|Response)\..*,^parameters\..*,^action:.*,^method:.*</param>
    </interceptor-ref>
    <interceptor-ref name="conversionError"/>
    <interceptor-ref name="validation">
        <param name="excludeMethods">input,back,cancel,browse</param>
    </interceptor-ref>
    <interceptor-ref name="workflow">
        <param name="excludeMethods">input,back,cancel,browse</param>
    </interceptor-ref>
</interceptor-stack>

Hope that helps.