javaxmljspstruts2tiles2

"org.apache.tiles.request.render.CannotRenderException: Cannot render an attribute that is not a string, toString returns: null" in Struts 2 app


I am working on a Struts 2 application that is integrated with the Tiles plugin. It works fine on my local computer. But when I deploy it to a UNIX server and try to navigate past the login page to an action that uses Tiles, I get a CannotRenderException. I am using Tomcat v9.

Here is the relevant code:

Tiles.xml

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE tiles-definitions PUBLIC   
    "-//Apache Software Foundation//DTD Tiles Configuration 2.0//EN"  
    "tiles-config_2_0.dtd"> 
 
 <tiles-definitions>  

    <definition name="no.header.layout" template="/tiles/layout/noHeader.jsp">
        <put-attribute name="title"         value="System" />
        <put-attribute name="includes"          value="/tiles/global/includes.jsp" />
        <put-attribute name="header"            value="/tiles/global/header.jsp" /> 
        <put-attribute name="body"              value="/tiles/global/blank.jsp" />
        <put-attribute name="footer"            value="/tiles/global/footer.jsp" />
    </definition>

        <definition name="home.page" extends="no.header.layout"><!--THIS IS THE PAGE I'M TRYING TO LOAD-->
        <put-attribute name="title" value="System Search"/>   
        <put-attribute name="body" value="/tiles/pages/home.jsp"/> 
        </definition>

</tiles-definitions>  

Struts.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
        "-//Apache Software Foundation//DTD Struts Configuration 6.0//EN"
        "struts-6.0.dtd">

<struts>

    <include file="struts-default.xml" />
    
    <constant name="struts.devMode" value="false" />

    <package name="default" extends="struts-default" namespace="/">
    
    <result-types>
            <result-type name = "tiles" 
            class="org.apache.struts2.views.tiles.TilesResult" />
        </result-types>

        <action name="home"> <!-- HERE IS THE ACTION I'M TRYING TO GET TO -->
        <result type="tiles">home.page</result>
    </action>

        <action name="login" class="bcs.actions.LoginAction">
        <result name="success" type="chain">home</result>
        <result name="failure">/login.jsp?message=LOGIN_FAILURE</result>
        <result name="failure2">/login.jsp?message=BC_LOGIN_FAILURE</result>
    </action>

    </package>

</struts>

noHeader.jsp

<%@ taglib uri = "http://tiles.apache.org/tags-tiles" prefix = "tiles"%>
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
<%@ taglib uri="/WEB-INF/struts-tags.tld" prefix="s" %>

<html>
<head>
    <meta http-equiv="X-UA-Compatible" content="IE=11">
    <title><tiles:getAsString name='title' /></title>
        
    <tiles:insertAttribute name='includes' flush='false' />
    
</head>
<body>

    <div class='container'>

        <div class='header_container'>
            <tiles:insertAttribute name='header' flush='false' />
        </div>
        
        <div class='noheader_container'>
            <tiles:insertAttribute name='body' flush='false' />         
        </div>
        
        <div class='footer_container'>
            <tiles:insertAttribute name='footer' flush='false' />       
        </div>
        
    </div>
    
</body>
</html>

Here are the dependencies I'm using:

antlr-2.7.2.jar
bsf-2.3.0.jar
commons-beanutils-1.8.0.jar
commons-chain-1.2.jar
commons-codec-1.14.jar
commons-collections.jar
commons-dbutils-1.1.jar
commons-digester-2.0.jar
commons-fileupload-1.5.jar
commons-io-2.11.0.jar
commons-lang3-3.12.0.jar
commons-logging-1.0.4.jar
commons-text-1.10.0.jar
commons-validator-1.3.1.jar
el-api-1.0.jar
encoder-1.2.3.jar
files.txt
freemarker-2.3.32.jar
httpclient-4.5.12.jar
httpcore-4.4.13.jar
iText-2.1.7.jar
javassist-3.29.0-GA.jar
javax.el-3.0.1-b12.jar
jcl-over-slf4j-1.7.6.jar
json-simple-1.1.1.jar
jsp-api-2.1.jar
jstl-1.0.2.jar
log4j-1.2.15.jar
log4j-api-2.20.0.jar
log4j-core-2.20.0.jar
ognl-3.3.4.jar
ojdbc6.jar
oro-2.0.8.jar
poi-3.2-FINAL-20081019.jar
poi-contrib-3.2-FINAL-20081019.jar
poi-scratchpad-3.2-FINAL-20081019.jar
servlet-api-2.5.jar
slf4j-api-2.0.7.jar
standard-1.0.6.jar
struts-el-1.3.10.jar
struts-extras-1.3.10.jar
struts-faces-1.3.10.jar
struts-mailreader-dao-1.3.10.jar
struts-scripting-1.3.10.jar
struts-taglib-1.3.10.jar
struts2-config-browser-plugin-6.2.0.jar
struts2-core-6.2.0.jar
struts2-tiles-plugin-6.2.0.jar
tiles-api-3.0.8.jar
tiles-autotag-core-runtime-1.2.jar
tiles-compat-3.0.8.jar
tiles-core-3.0.8.jar
tiles-el-3.0.8.jar
tiles-freemarker-3.0.8.jar
tiles-jsp-3.0.8.jar
tiles-ognl-3.0.8.jar
tiles-request-api-1.0.7.jar
tiles-request-freemarker-1.0.7.jar
tiles-request-jsp-1.0.7.jar
tiles-request-servlet-1.0.7.jar
tiles-servlet-3.0.8.jar
tiles-template-3.0.8.jar
xercesImpl-2.12.2.jar
xwork-2.1.2.jar

And here is the exception:

 ERROR org.apache.struts2.dispatcher.DefaultDispatcherErrorHandler - Exception occurred during processing request: Cannot render an attribute that is not a string, toString returns: null
org.apache.tiles.request.render.CannotRenderException: Cannot render an attribute that is not a string, toString returns: null
        at org.apache.tiles.impl.BasicTilesContainer.render(BasicTilesContainer.java:255) ~[tiles-core-3.0.8.jar:3.0.8]
        at org.apache.tiles.impl.BasicTilesContainer.render(BasicTilesContainer.java:397) ~[tiles-core-3.0.8.jar:3.0.8]
        at org.apache.tiles.impl.BasicTilesContainer.render(BasicTilesContainer.java:238) ~[tiles-core-3.0.8.jar:3.0.8]
        at org.apache.tiles.TilesContainerWrapper.render(TilesContainerWrapper.java:103) ~[tiles-api-3.0.8.jar:3.0.8]
        at org.apache.tiles.impl.mgmt.CachingTilesContainer.render(CachingTilesContainer.java:126) ~[tiles-core-3.0.8.jar:3.0.8]
        at org.apache.struts2.views.tiles.TilesResult.doExecute(TilesResult.java:158) ~[struts2-tiles-plugin-6.2.0.jar:6.2.0]
        at org.apache.struts2.result.StrutsResultSupport.execute(StrutsResultSupport.java:206) ~[struts2-core-6.2.0.jar:6.2.0]
        at com.opensymphony.xwork2.DefaultActionInvocation.executeResult(DefaultActionInvocation.java:377) ~[struts2-core-6.2.0.jar:6.2.0]
        at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:284) ~[struts2-core-6.2.0.jar:6.2.0]
        at bcs.actions.SessionPreProcessor.intercept(SessionPreProcessor.java:53) ~[classes/:?]
        at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:256) ~[struts2-core-6.2.0.jar:6.2.0]
        at org.apache.struts2.interceptor.debugging.DebuggingInterceptor.intercept(DebuggingInterceptor.java:256) ~[struts2-core-6.2.0.jar:6.2.0]
        at com.opensymphony.xwork2.DefaultActionInvocation.executeConditional(DefaultActionInvocation.java:299) ~[struts2-core-6.2.0.jar:6.2.0]
        at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:253) ~[struts2-core-6.2.0.jar:6.2.0]
        at com.opensymphony.xwork2.interceptor.DefaultWorkflowInterceptor.doIntercept(DefaultWorkflowInterceptor.java:179) ~[struts2-core-6.2.0.jar:6.2.0]
        at com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:99) ~[struts2-core-6.2.0.jar:6.2.0]
        at com.opensymphony.xwork2.DefaultActionInvocation.executeConditional(DefaultActionInvocation.java:299) ~[struts2-core-6.2.0.jar:6.2.0]
        at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:253) ~[struts2-core-6.2.0.jar:6.2.0]
        at com.opensymphony.xwork2.validator.ValidationInterceptor.doIntercept(ValidationInterceptor.java:263) ~[struts2-core-6.2.0.jar:6.2.0]
        at org.apache.struts2.interceptor.validation.AnnotationValidationInterceptor.doIntercept(AnnotationValidationInterceptor.java:49) ~[struts2-core-6.2.0.jar:6.2.0]
        at com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:99) ~[struts2-core-6.2.0.jar:6.2.0]
        at com.opensymphony.xwork2.DefaultActionInvocation.executeConditional(DefaultActionInvocation.java:299) ~[struts2-core-6.2.0.jar:6.2.0]
        at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:253) ~[struts2-core-6.2.0.jar:6.2.0]
        at org.apache.struts2.interceptor.FetchMetadataInterceptor.intercept(FetchMetadataInterceptor.java:78) ~[struts2-core-6.2.0.jar:6.2.0]
        at com.opensymphony.xwork2.DefaultActionInvocation.executeConditional(DefaultActionInvocation.java:299) ~[struts2-core-6.2.0.jar:6.2.0]
        at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:253) ~[struts2-core-6.2.0.jar:6.2.0]
        at org.apache.struts2.interceptor.CoopInterceptor.intercept(CoopInterceptor.java:57) ~[struts2-core-6.2.0.jar:6.2.0]
        at com.opensymphony.xwork2.DefaultActionInvocation.executeConditional(DefaultActionInvocation.java:299) ~[struts2-core-6.2.0.jar:6.2.0]
        at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:253) ~[struts2-core-6.2.0.jar:6.2.0]
        at org.apache.struts2.interceptor.CoepInterceptor.intercept(CoepInterceptor.java:55) ~[struts2-core-6.2.0.jar:6.2.0]
        at com.opensymphony.xwork2.DefaultActionInvocation.executeConditional(DefaultActionInvocation.java:299) ~[struts2-core-6.2.0.jar:6.2.0]
        at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:253) ~[struts2-core-6.2.0.jar:6.2.0]
        at com.opensymphony.xwork2.interceptor.ConversionErrorInterceptor.doIntercept(ConversionErrorInterceptor.java:143) ~[struts2-core-6.2.0.jar:6.2.0]
        at com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:99) ~[struts2-core-6.2.0.jar:6.2.0]
        at com.opensymphony.xwork2.DefaultActionInvocation.executeConditional(DefaultActionInvocation.java:299) ~[struts2-core-6.2.0.jar:6.2.0]
        at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:253) ~[struts2-core-6.2.0.jar:6.2.0]
        at com.opensymphony.xwork2.interceptor.ParametersInterceptor.doIntercept(ParametersInterceptor.java:152) ~[struts2-core-6.2.0.jar:6.2.0]
        at com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:99) ~[struts2-core-6.2.0.jar:6.2.0]
        at com.opensymphony.xwork2.DefaultActionInvocation.executeConditional(DefaultActionInvocation.java:299) ~[struts2-core-6.2.0.jar:6.2.0]
        at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:253) ~[struts2-core-6.2.0.jar:6.2.0]
        at com.opensymphony.xwork2.interceptor.ParametersInterceptor.doIntercept(ParametersInterceptor.java:152) ~[struts2-core-6.2.0.jar:6.2.0]
        at com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:99) ~[struts2-core-6.2.0.jar:6.2.0]

I have tried commenting out attributes in both tiles.xml and noHeader.jsp, omitting a custom interceptor that I have, using different tiles-config dtd files, and checking the spelling of the templates and values. I have also made sure the app server has the necessary permissions. Other than that, I'm not sure what else to try.


Solution

  • There was an old tiles-defs.xml file left over from when the project was using Struts 1.3 instead of 2. Once I deleted that file and the associated tiles-config_1_3.dtd, the issue went away. Although I'm curious as to why the old xml file would affect the Struts 2 code.