java.lang.IllegalStateException:CDATA 标记不能嵌套

Posted

技术标签:

【中文标题】java.lang.IllegalStateException:CDATA 标记不能嵌套【英文标题】:java.lang.IllegalStateException: CDATA tags may not nest 【发布时间】:2012-08-07 18:26:46 【问题描述】:

我遇到了 JSF 页面中的 ajax 请求问题。当我点击按钮时,我得到了这个异常:

SEVERE: Servlet.service() for servlet Faces Servlet threw exception
java.lang.IllegalStateException: CDATA tags may not nest
    at com.sun.faces.renderkit.html_basic.HtmlResponseWriter.startCDATA(HtmlResponseWriter.java:630)
    at javax.faces.context.ResponseWriterWrapper.startCDATA(ResponseWriterWrapper.java:172)
    at javax.faces.context.PartialResponseWriter.startError(PartialResponseWriter.java:342)
    at org.primefaces.context.PrimePartialResponseWriter.startError(PrimePartialResponseWriter.java:210)
    at com.sun.faces.context.AjaxExceptionHandlerImpl.handlePartialResponseError(AjaxExceptionHandlerImpl.java:200)
    at com.sun.faces.context.AjaxExceptionHandlerImpl.handle(AjaxExceptionHandlerImpl.java:123)
    at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:119)
    at com.sun.faces.lifecycle.LifecycleImpl.render(LifecycleImpl.java:139)

我认为String 对象存在一些问题,因为当我对网站上显示的 JPA 实体属性进行硬编码时,一切正常。但是,当从数据库 (PostgreSQL) 中检索实体时,它会引发上述异常。

JSF 代码:

<p:column>
    <f:facet name="header">
        Akcja
    </f:facet>
    <h:commandButton actionListener="#mBDocumentMigration.actionEdit(object)" value="Edytuj" rendered="#mBDocumentMigration.editingObject == null" >
        <f:ajax render="@form" execute="@form" />
    </h:commandButton>
    <h:commandButton action="#mBDocumentMigration.actionZapisz" value="Zapisz" rendered="#mBDocumentMigration.editingObject != null" >
    <f:ajax render="@form"  execute="@this" />
    </h:commandButton>
</p:column>

【问题讨论】:

我猜您的问题出在您的 JSf 代码中的其他地方,而不是您发布的片段中。 【参考方案1】:

在呈现 JSF 响应期间抛出异常,这是由代码中的错误引起的。然而,Mojarra 又未能使用内置的 ajax 异常处理程序正确处理此异常,从而导致您现在看到的另一个异常,隐藏了有关原始异常的所有细节。

仔细查看堆栈跟踪。从底部开始跟踪调用堆栈:

at com.sun.faces.lifecycle.LifecycleImpl.render(LifecycleImpl.java:139)

因此,它发生在渲染响应阶段。好的,看下一行(上面的那个):

at com.sun.faces.context.AjaxExceptionHandlerImpl.handle(AjaxExceptionHandlerImpl.java:123)

嘿,它已通过 Mojarra 的内置 ajax 异常处理程序 AjaxExceptionHandlerImpl 传递! 在 ajax 请求期间发生异常时调用。好的,从下到上进一步阅读下一行:

at com.sun.faces.renderkit.html_basic.HtmlResponseWriter.startCDATA(HtmlResponseWriter.java:630)
at javax.faces.context.ResponseWriterWrapper.startCDATA(ResponseWriterWrapper.java:172)
at javax.faces.context.PartialResponseWriter.startError(PartialResponseWriter.java:342)
at org.primefaces.context.PrimePartialResponseWriter.startError(PrimePartialResponseWriter.java:210)
at com.sun.faces.context.AjaxExceptionHandlerImpl.handlePartialResponseError(AjaxExceptionHandlerImpl.java:200)

因此尝试将错误信息写入 ajax 响应。此信息必须放在 CDATA 块中。但是,启动 CDATA 块失败如下,因为显然已经有一个 CDATA 块打开:

java.lang.IllegalStateException: CDATA tags may not nest

这反过来表明在编写 ajax 响应期间发生了异常,很可能是因为您在仅在生成 HTML 输出期间调用的 getter 方法中执行业务逻辑。所以这个过程很可能是这样的:

    JSF 进入 RENDER_RESPONSE 阶段。 JSF 需要生成 HTML 输出。 对于每个&lt;f:ajax render="some"&gt;(或&lt;p:ajax update="some"&gt;),它需要创建一个&lt;update id="some"&gt; XML 块,其中生成的HTML 输出位于CDATA block 内(以保持XML 输出在语法上有效)。所以需要启动一个 CDATA 块。 在将 HTML 输出生成到 CDATA 块中时,会评估所有呈现时 EL 表达式,包括所有 UI 组件的 value 属性。 某处,EL 表达式背后的 getter 引发了由您自己的代码中的错误引起的异常。 JSF 立即停止生成 HTML 输出并且没有关闭 CDATA 块。 HTTP 响应包含半生不熟的数据。 AjaxExceptionHandlerImpl 被触发。 AjaxExceptionHandlerImpl 需要将异常/错误详细信息写入响应。但是,它没有检查响应是否已经写入。它盲目地尝试打开一个 CDATA 块,该块又失败了,因为它已经打开了。它抛出了你看到的异常,隐藏了它试图处理的真正底层异常的所有细节。

如您所见,问题有两个方面:

    JSF 渲染器不应该让响应半途而废。 Mojarra 的 AjaxExceptionHandlerImpl 应该已经检查/验证了响应的状态。

如果您将 Mojarra 的内置 ajax 异常处理程序替换为 custom one which immediately prints the stack trace 或 OmniFaces FullAjaxExceptionHandler which is capable of detecting and cleaning halfbaked ajax responses,那么它将最终揭示并显示由代码中的错误引起的真正底层。如前所述,很可能是由performing business logic in a getter method, which is a bad practice 引起的。

【讨论】:

-1 那么,解决方案是什么?我们应该怎么做才能防止发生这种异常?我遇到了这个例外,除非您在 JSF 内部排除对我完全没用的内容,而且我认为对于大多数只是试图修复错误的开发人员来说,否则我看不到您的回答可能会有什么帮助。你很不清楚...... @St.Antario:我为不耐烦的人添加了一个 tldr。 好的,问题是当我们使用 JSF 1.2 时代码运行良好。更新到 JSF 2.1 后,它开始抛出此异常。是严格的 JSF 2.x 问题吗?【参考方案2】:

我遇到了和你一样的问题,当我使用支持 bean 中的自动完成组件绑定时,它工作正常。

<p:autoComplete id="autocomplete" binding="#searchBean.compui" title="Find" value="#searchBean.searchfor" forceSelection="false" queryDelay="30" dropdown="true" maxResults="20" emptyMessage="None" completeMethod="#searchBean.complete" style="width: 90%;"/>
<p:commandButton id="cmdsearch" value="#msg.search" action="#searchBean.search" update="tblprocresults" icon="ui-icon-zoomin"/>

在后台bean中

private AutoComplete compui;
//compui is initialized when bean is constructed
    public AutoComplete getCompui() 
            return compui;
        

        public void setCompui(AutoComplete compui) 
            this.compui = compui;
        

【讨论】:

不需要初始化compui(自动完成绑定组件)【参考方案3】:

CDATA 问题不是 PrimeFaces 问题,而是与负责提供部分输出的 JSF 实现有关。 替换为它的 JSF 属性;)

【讨论】:

【参考方案4】:

如果您在服务器日志中还看到java.lang.ClassCastException: com.sun.faces.facelets.compiler.UIInstructions cannot be cast to org.primefaces.component.tree.UITreeNode,请添加

<context-param>
  <param-name>javax.faces.FACELETS_SKIP_COMMENTS</param-name>
  <param-value>true</param-value>
</context-param>

/WEB-INF/web.xml

【讨论】:

【参考方案5】:

只是为了考虑一些事情,有时它可能是一个非常愚蠢的错误。

例如,如果我忘记在 xhtml 页面正在使用的一个 bean 中初始化 ArrayList,有时我会收到同样的错误消息:

我会这样做:

List<String> myList;

但忘了这样做:

myList = new ArrayList();

因此,作为另一件需要考虑的事情,请确保您已经处理好所有的家务(确保变量已初始化/填充等...)

【讨论】:

【参考方案6】:

我在Tomcat 7中摆脱类似执行的经验是:如果你在jsf中调用一个方法,你将不得不添加(),即使它没有参数。

这个异常但不会出现,如果你使用的是码头

编辑:即使这个异常被消除了,它也给出了另一个异常:

java.lang.NoSuchMethodError: javax.el.ELResolver.invoke(Ljavax/el/ELContext;Ljava/lang/Object;Ljava/lang/Object;[Ljava/lang/Class;[Ljava/lang/Object;)Ljava/lang/Object;

经过研究,我发现 Tomcat 7 本身带来了 EL 依赖,因此 pom.xml 中的任何其他依赖都像

<dependency>
    <groupId>javax.el</groupId>
    <artifactId>el-api</artifactId>
    <version>2.2</version>
    <scope>provided</scope>
</dependency>

应移除以避免混合。

之后,您必须在 Eclipse 中通过 Run as - tomcat7:run 启动 tomcat7,而不是默认启动 tomcat 6 的tomcat:run

我的环境:

Eclipse 开普勒 JDK 1.7.0_45 Maven 3.1.1

【讨论】:

以上是关于java.lang.IllegalStateException:CDATA 标记不能嵌套的主要内容,如果未能解决你的问题,请参考以下文章