简体   繁体   中英

JSF render template to text/plain

I've got a (working) JSF app. Now i want a document that is not a page resource (let's say a plain text document) to be expanded using the JSF expression language (as i need state from my beans and this seems "natural"). But it seems i don't get completely the mechanics...

In order to work on "*.txt" files if have in web.xml

<servlet-mapping>
    <servlet-name>Faces Servlet</servlet-name>
    <url-pattern>*.xhtml</url-pattern>
</servlet-mapping> 
<servlet-mapping>
    <servlet-name>Faces Servlet</servlet-name>
    <url-pattern>*.txt</url-pattern>
</servlet-mapping>

I add a "plain.txt" resource

<f:view contentType="text/plain" xmlns:f="http://java.sun.com/jsf/core" >

hi #{request.contextPath}/

</f:view>

Requesting

http://myServer/myApp/plain.txt

failed with an endless loop on the server with myfaces 2.1.10. Renaming the resource to "plain.xhtml" and requesting it worked fine.

After updating to myfaces 2.1.17 (same on 2.2.9), serving "plain.txt" worked, too!

The question is: is using templates this way "best practice" or "accidental side effect" (and should not really work)? Is there a better way to implement such a templating task?

EDIT

After some more attempts it seems even more odd. Serving the expanded content seems to fail at random (both 2.1.17 and 2.2.9). After restarting i get the "loop" on the server side for one resource "res1.txt", but not for an exact copy of the resource "res2.txt". It also happened that "res1.txt" was rendered succesfully. Maybe the server traces can give some hint:

First, there's a very long "recursion"

Mär 09, 2016 5:06:20 PM org.apache.catalina.core.ApplicationDispatcher invoke
SCHWERWIEGEND: Servlet.service() for servlet Faces Servlet threw exception
java.lang.NullPointerException
    at org.apache.myfaces.shared.context.flash.FlashImpl.isKeepMessages(FlashImpl.java:388)
    at org.apache.myfaces.shared.context.flash.FlashImpl._saveMessages(FlashImpl.java:668)
    at org.apache.myfaces.shared.context.flash.FlashImpl.doPostPhaseActions(FlashImpl.java:269)
    at org.apache.myfaces.lifecycle.LifecycleImpl.render(LifecycleImpl.java:254)
    at javax.faces.webapp.FacesServlet.service(FacesServlet.java:199)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:748)
    at org.apache.catalina.core.ApplicationDispatcher.processRequest(ApplicationDispatcher.java:486)
    at org.apache.catalina.core.ApplicationDispatcher.doForward(ApplicationDispatcher.java:411)
    at org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:338)
    at org.apache.myfaces.context.servlet.ServletExternalContextImpl.dispatch(ServletExternalContextImpl.java:369)
    at org.apache.myfaces.view.jsp.JspViewDeclarationLanguage.buildView(JspViewDeclarationLanguage.java:99)
    at org.apache.myfaces.lifecycle.RenderResponseExecutor.execute(RenderResponseExecutor.java:78)
    at org.apache.myfaces.lifecycle.LifecycleImpl.render(LifecycleImpl.java:241)
    at javax.faces.webapp.FacesServlet.service(FacesServlet.java:199)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:748)
    at org.apache.catalina.core.ApplicationDispatcher.processRequest(ApplicationDispatcher.java:486)
    at org.apache.catalina.core.ApplicationDispatcher.doForward(ApplicationDispatcher.java:411)
    at org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:338)
    at org.apache.myfaces.context.servlet.ServletExternalContextImpl.dispatch(ServletExternalContextImpl.java:369)
    at org.apache.myfaces.view.jsp.JspViewDeclarationLanguage.buildView(JspViewDeclarationLanguage.java:99)
    at org.apache.myfaces.lifecycle.RenderResponseExecutor.execute(RenderResponseExecutor.java:78)
    at org.apache.myfaces.lifecycle.LifecycleImpl.render(LifecycleImpl.java:241)
    at javax.faces.webapp.FacesServlet.service(FacesServlet.java:199)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:748)
    at org.apache.catalina.core.ApplicationDispatcher.processRequest(ApplicationDispatcher.java:486)
    at org.apache.catalina.core.ApplicationDispatcher.doForward(ApplicationDispatcher.java:411)
    at org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:338)
    at org.apache.myfaces.context.servlet.ServletExternalContextImpl.dispatch(ServletExternalContextImpl.java:369)
    at org.apache.myfaces.view.jsp.JspViewDeclarationLanguage.buildView(JspViewDeclarationLanguage.java:99)
    at org.apache.myfaces.lifecycle.RenderResponseExecutor.execute(RenderResponseExecutor.java:78)
    at org.apache.myfaces.lifecycle.LifecycleImpl.render(LifecycleImpl.java:241)
    at javax.faces.webapp.FacesServlet.service(FacesServlet.java:199)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:748)
    at org.apache.catalina.core.ApplicationDispatcher.processRequest(ApplicationDispatcher.java:486)
    at org.apache.catalina.core.ApplicationDispatcher.doForward(ApplicationDispatcher.java:411)
    at org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:338)
    at org.apache.myfaces.context.servlet.ServletExternalContextImpl.dispatch(ServletExternalContextImpl.java:369)
    at org.apache.myfaces.view.jsp.JspViewDeclarationLanguage.buildView(JspViewDeclarationLanguage.java:99)
    at org.apache.myfaces.lifecycle.RenderResponseExecutor.execute(RenderResponseExecutor.java:78)
    at org.apache.myfaces.lifecycle.LifecycleImpl.render(LifecycleImpl.java:241)
    at javax.faces.webapp.FacesServlet.service(FacesServlet.java:199)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:748)
    at org.apache.catalina.core.ApplicationDispatcher.processRequest(ApplicationDispatcher.java:486)
    at org.apache.catalina.core.ApplicationDispatcher.doForward(ApplicationDispatcher.java:411)
    at org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:338)
    at org.apache.myfaces.context.servlet.ServletExternalContextImpl.dispatch(ServletExternalContextImpl.java:369)
    at org.apache.myfaces.view.jsp.JspViewDeclarationLanguage.buildView(JspViewDeclarationLanguage.java:99)
    at org.apache.myfaces.lifecycle.RenderResponseExecutor.execute(RenderResponseExecutor.java:78)
    at org.apache.myfaces.lifecycle.LifecycleImpl.render(LifecycleImpl.java:241)
    at javax.faces.webapp.FacesServlet.service(FacesServlet.java:199)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:748)
    at org.apache.catalina.core.ApplicationDispatcher.processRequest(ApplicationDispatcher.java:486)
    at org.apache.catalina.core.ApplicationDispatcher.doForward(ApplicationDispatcher.java:411)
    at org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:338)
    at org.apache.myfaces.context.servlet.ServletExternalContextImpl.dispatch(ServletExternalContextImpl.java:369)
    at org.apache.myfaces.view.jsp.JspViewDeclarationLanguage.buildView(JspViewDeclarationLanguage.java:99)
    at org.apache.myfaces.lifecycle.RenderResponseExecutor.execute(RenderResponseExecutor.java:78)
    at org.apache.myfaces.lifecycle.LifecycleImpl.render(LifecycleImpl.java:241)
    at javax.faces.webapp.FacesServlet.service(FacesServlet.java:199)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:748)
    at org.apache.catalina.core.ApplicationDispatcher.processRequest(ApplicationDispatcher.java:486)
    at org.apache.catalina.core.ApplicationDispatcher.doForward(ApplicationDispatcher.java:411)
    at org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:338)
    at org.apache.myfaces.context.servlet.ServletExternalContextImpl.dispatch(ServletExternalContextImpl.java:369)
    at org.apache.myfaces.view.jsp.JspViewDeclarationLanguage.buildView(JspViewDeclarationLanguage.java:99)
    at org.apache.myfaces.lifecycle.RenderResponseExecutor.execute(RenderResponseExecutor.java:78)
    at org.apache.myfaces.lifecycle.LifecycleImpl.render(LifecycleImpl.java:241)
    at javax.faces.webapp.FacesServlet.service(FacesServlet.java:199)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:748)
    at org.apache.catalina.core.ApplicationDispatcher.processRequest(ApplicationDispatcher.java:486)
    at org.apache.catalina.core.ApplicationDispatcher.doForward(ApplicationDispatcher.java:411)
    at org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:338)
    at org.apache.myfaces.context.servlet.ServletExternalContextImpl.dispatch(ServletExternalContextImpl.java:369)
    at org.apache.myfaces.view.jsp.JspViewDeclarationLanguage.buildView(JspViewDeclarationLanguage.java:99)
    at org.apache.myfaces.lifecycle.RenderResponseExecutor.execute(RenderResponseExecutor.java:78)
    at org.apache.myfaces.lifecycle.LifecycleImpl.render(LifecycleImpl.java:241)
    at javax.faces.webapp.FacesServlet.service(FacesServlet.java:199)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:220)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:505)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:170)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:103)
    at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:956)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:423)
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1079)
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:625)
    at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:318)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:745)

This trace is repeated and get shorter each time until it comes eventually to this end:

Mär 09, 2016 5:06:20 PM org.apache.catalina.core.StandardWrapperValve invoke
SCHWERWIEGEND: Servlet.service() for servlet [Faces Servlet] in context with path [/cloudsuite-snippets] threw exception
java.lang.NullPointerException
    at org.apache.myfaces.shared.context.flash.FlashImpl.isKeepMessages(FlashImpl.java:388)
    at org.apache.myfaces.shared.context.flash.FlashImpl._saveMessages(FlashImpl.java:668)
    at org.apache.myfaces.shared.context.flash.FlashImpl.doPostPhaseActions(FlashImpl.java:269)
    at org.apache.myfaces.lifecycle.LifecycleImpl.render(LifecycleImpl.java:254)
    at javax.faces.webapp.FacesServlet.service(FacesServlet.java:199)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:220)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:505)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:170)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:103)
    at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:956)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:423)
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1079)
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:625)
    at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:318)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:745)

The browser page is simply white..

EDIT II

And another information: The failure is not really indeterministic (which would have damaged my faith a little bit ...) but nonetheless strange. Rendering succeeded for all resources where i had a resource with the same name, but the ending ".xhtml".

The server seems to redirect internally to the ".xhtml" version, render the content of this one and return it with the name of the ".txt" version (ignoring the content of the latter). What's going on here?

The question is: is using templates this way "best practice" or "accidental side effect" (and should not really work)?

It should not really work. JSF as being a HTML form based MVC framework doesn't support text/plain content in first place, but only HTML based ones such as text/html . When trying your use case on Mojarra instead of MyFaces (after having renamed plain.txt to plain.xhtml , see later), I get below exception:

java.lang.IllegalArgumentException: Unrecognized Content Type.

As to view resolving, technically you should have named your plain.txt file as plain.xhtml and only have added *.txt URL pattern to FacesServlet . The FacesServlet namely locates the physical view after remapping any file extension in URL pattern against javax.faces.DEFAULT_SUFFIX context parameter which defaults to .xhtml . When the physical file is absent, JSF will fall back from Facelets views to JSP views (which is confirmed by presence of JspViewDeclarationLanguage in stack trace) but as plain.jsp also doesn't exist and the URL still matches the FacesServlet mapping this in turn runs in an infinite loop in a fruitless attempt to find and render the physical resource.

For an elaborate explanation of the inner workings of JSF view resolving, see also JSF Facelets: Sometimes I see the URL is .jsf and sometimes .xhtml. Why? As to the infinite loop on dispatch() , this is in turn technically a bug in JSF as it should really have returned a 404 instead.


Is there a better way to implement such a templating task?

If the sole purpose is evaluating EL expressions, then better use JSP servlet instead of the JSF servlet and exclusively use read-only EL expressions ${} instead of writable EL expressions #{} because this isn't supported by JSP.

The simplest way is to change the servlet name to be the one of Tomcat's own JSP servlet: jsp (a <servlet> entry is not necessary as Tomcat has already definied it in its own /conf/web.xml ).

<servlet-mapping>
    <servlet-name>jsp</servlet-name>
    <url-pattern>*.txt</url-pattern>
</servlet-mapping>

Then alter the /plain.txt as a JSP view as below. Note that ${request} doesn't exist in JSP, it's only available as property of ${pageContext} . See also our EL wiki page .

<%@page contentType="text/plain" %>

hi ${pageContext.request.contextPath}/

As to resolving managed beans, it only doesn't discover JSF @ManagedBean , but it can discover CDI @Named . So when you install CDI on Tomcat and rework the managed beans of interest to be CDI managed beans instead of JSF managed beans, then they will be available.

A different alternative which requires a little bit more work but is guaranteed portable across servletcontainers (because the servlet name of jsp is Tomcat specific although almost all servletcontainers have the same "de facto" name) is using a plain vanilla servlet or perhaps even a JAX-RS (RESTful) resource for the task you had in mind. Map it to the desired URL pattern of /plain.txt , let it prepare the desired models and finally forward to a "real" JSP file the usual way .

@WebServlet("/plain.txt")
public class PlainTxtServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        request.getRequestDispatcher("/WEB-INF/plain.jsp").forward(request, response);
    }

}

And have a /WEB-INF/plain.jsp as physical file, still with the abovementioned JSP syntax. Note that it's placed inside /WEB-INF as you don't want the enduser to be able to directly request /plain.jsp instead of /plain.txt .

Additional advantage is that this doesn't necessarily require CDI for managing beans as you can manually manage beans like below:

Bean bean = new Bean();
request.setAttribute("bean", bean);

It'll be available as ${bean} in EL.

See also:

I am not quite sure what is going on there but here is how I did it...

Leave your web.xml file as you already have it.

create xhtm file like plain.xhtml like so...

<ui:composition
    xmlns:ui="http://java.sun.com/jsf/facelets">
    hi #{request.contextPath}/
</ui:composition>

now go to .../plain.txt

it should work just fine

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM