non-Faces request generates Faces response -- how-to?

Hi crew, I have a question that might be kind of basic.

I'd like to receive a non-Faces request and return a Faces response.

I've followed the steps in the 1.1 spec, and it works, but... when I submit a Faces request from the Faces page, its servlet-path is completely wrong, and I get a "resource not available" error from JBoss (4.0.1).

The problem is the incoming request is something like "/servlet/SomeServlet?...". My Faces JSPs should be invoked via "/faces/Some.jsp". The Faces response seems to get generated ok, but when I submit from the Faces page, the request goes to "/servlet/SomeServlet/Some.jsp" and the webapp container fails it (http status 404).

Do I need to set the javax.servlet.forward.servlet_path request attribute (and related attributes defined in section 8.4.2 of the servlet 2.4 spec? That seems like a hack. This must be a problem that's been solved a million times already. I just can't find it w/a search.

Here's my code.

protectedvoid doGet( HttpServletRequest aRequest,

HttpServletResponse aResponse)

{

String requestedView = aRequest.getParameter( PARAM_VIEW);

// Section references are from JSF 1.1 spec.

// Acquire refs to Faces objects

// Sect. 2.4.1.1

LifecycleFactory lifecycleFactory

= (LifecycleFactory) FactoryFinder.getFactory(

FactoryFinder.LIFECYCLE_FACTORY);

Lifecycle lifecycle = lifecycleFactory.getLifecycle(

LifecycleFactory.DEFAULT_LIFECYCLE);

// 2.4.1.2

FacesContextFactory facesContextFactory

= (FacesContextFactory) FactoryFinder.getFactory(

FactoryFinder.FACES_CONTEXT_FACTORY);

FacesContext facesCtx = facesContextFactory.getFacesContext(

getServletContext(),

aRequest,

aResponse,

lifecycle);

// Create new view

// 2.4.2.1

Application app = facesCtx.getApplication();

ViewHandler viewHdlr = app.getViewHandler();

UIViewRoot viewRoot = viewHdlr.createView( facesCtx, requestedView);

// 2.4.2.2: we accept the default renderkit

// 2.4.2.3: we'll let Faces handle the

//component tree

// 2.4.2.4: store view into context

facesCtx.setViewRoot( viewRoot);

lifecycle.render( facesCtx);

}

[2717 byte] By [JohnLusk4a] at [2007-10-2 5:47:06]
# 1
Is requestedView a path to a particular jsf page, such as /application/faces/response.jsp, in which response.jsp is populated with jsf tags?
Elam_Dalya at 2007-7-16 1:56:46 > top of Java-index,Enterprise & Remote Computing,Web Tier APIs...
# 2

Well, I somewhat found an answer to this.

I'm using the Oracle jsf implementation, and one of the properties available to the servlet request object is com.sun.faces.INVOCATION_PATH, which if I set the alias of my Faces servlet, then all the paths in my JSF page are correct.

But, my understanding here is that the com.sun.faces package is vendor specific, so this may not work with other JSF implementations and may change in the one I'm using now.

Is there a JSF agnostic way to do this?

Thanks,

-Elam

Elam_Dalya at 2007-7-16 1:56:46 > top of Java-index,Enterprise & Remote Computing,Web Tier APIs...
# 3

There is some code to do this in the Tobago project.

The NonFacesRequestServlet.java class.

The part you're missing is ..

facesContext.setViewRoot(view);

This will get you to the page you want. BUT.. this is where I'm stuck. my command buttons on the page do not work because the URL path contains the /servlet/view/jsp/testpage.jsf instead of /jsp/testpage.jsf

Hope this is some help, if find a compelte soultion, then please let me know.

Jeff

p.s. what I set in setViewRoot, is "/jsp/view.jsp" and faces-config.xml

has ...

<navigation-rule>

<from-view-id>/jsp/view.jsp</from-view-id>

<navigation-case>

<from-outcome>showPage</from-outcome>

<to-view-id>/jsp/testpage.jsp</to-view-id>

</navigation-case>

</navigation-rule>

Source code...

http://svn.apache.org/viewcvs.cgi/incubator/tobago/trunk/tobago-core/src/main/java/org/apache/myfaces/tobago/servlet/NonFacesRequestServlet.java?view=markup

jeff_portera at 2007-7-16 1:56:46 > top of Java-index,Enterprise & Remote Computing,Web Tier APIs...
# 4

Hi jeff,

I figured the setView root part out a while ago, but was stuck on the same thing as you are now. That was why I posted the INVOCATION_PATH property of the request servlet. If you set that, then all your paths will be correct when your JSF view is rendered.

Since the INVOCATION_PATH property is a property of the Request object, then I'm thinking that it may be servlet vendor specific, as I'm using Tomcat 5.0.28. So I don't know if that would work for any other servlet containers. It does work with all 3 JSF implementations I tried, JSF 1.1, Myfaces 1.1.1, and ADF EA 19.

What's annoying is that Sun spells out the non-faces request/faces response in the JSF spec, but then gives no solution to the problem at hand when you use that scenario. What's the point?

Elamda at 2007-7-16 1:56:46 > top of Java-index,Enterprise & Remote Computing,Web Tier APIs...
# 5

Yeah, kinda frustratin', ain't it?

btw, my "finesse" of the problem is to simply make sure that we enter Faces pages via a GET request, never POST. So, for example, if I need to tack on a new JSF page to a part of the app that's already been written w/old-school JSP/servlet, I just make sure to generate a URL on the old page that the user must click on to GET the Faces page. Then, once I'm in the Faces part of the app, everything's cool.

John.

JohnLusk4a at 2007-7-16 1:56:46 > top of Java-index,Enterprise & Remote Computing,Web Tier APIs...
# 6

> Is requestedView a path to a particular jsf page,

> such as /application/faces/response.jsp, in which

> response.jsp is populated with jsf tags?

Yes, exactly. It can be arrived at however you want. I chose to have it as a parameter in the request (although that was a silly attempt -- if I'm futzing w/the request params, I might as well just code a GET url, see my other response today).

John.

JohnLusk4a at 2007-7-16 1:56:46 > top of Java-index,Enterprise & Remote Computing,Web Tier APIs...
# 7

There is another discussion of this issue going on in the myFaces user group

Volker, Matthias and Udo are looking into the problem.

The subject title is : Re: myFaces - servlet redirect.

http://www.mail-archive.com/users%40myfaces.apache.org/msg15139.html

Over view:...

Its all to do with the servlet-mapping in the web.xml & the container you are using: Sun & tomcat differ.

web.xml snip:

<!--for sun ri (tested with version 1.1.01)-->

<url-pattern>/FishServlet</url-pattern>

<!--for myfaces (tested with version 1.1.1)-->

<url-pattern>/faces/FishServlet</url-pattern>

Udo said ...

"to the difference of myfaces and the sun-ri:

I've setup an example here http://svn.apache.org/repos/asf/incubator/tobago/trunk/tobago-example/tobago-example-nonfacesrequest/"

Hope this info helps someone else out as well.

jeff_portera at 2007-7-16 1:56:46 > top of Java-index,Enterprise & Remote Computing,Web Tier APIs...
# 8

Hi all,

I have been battling this problem for about 4 days.

I am pretty sure I understand what is happening, why, and therefore how to make it work.

I am assuming you have code similar to that given elsewhere in this discussion, and based on the "non faces request with faces response" section of the JSF Spec.

What happens with a non faces request to faces response

When you have finished procesing the non faces request, found the faces response and faces is rendering it, then faces needs to create a URL to point back at that page when you submit. Faces uses ViewHandler.getActionURL() to create this URL.

But, the servlet API has no facility to access the servlet mappings, and neither does faces API provide a mechanism to discover the mapping.

Therefore ViewHandler.getActionURL assumes that the current request has come through the servlet mapping for the faces servlet, and by looking at the servletpath and additional path info for the request it determines what mapping was used, and generates a URL to point to the reponse page via that same mapping.

But

In this case the request didn't go through the faces servlet.

So what we need to do (additional to the steps in the JSF spec) is create a mapping for our non faces servlet that is very similar to the faces servlet mapping, in order that getActionURL() comes up with the right answer, even tho' its using the wrong data.

This means we CANNOT USE PREFIX MAPPING for the faces servlet or the non faces servlet, because to make getActionURL work correctly the mappings would have to be exactly the same. And the same prefix mapping can't point to two different servlets.

Therefore you must use suffix mapping. The non faces servlet has an explicit URL mapping to the servlet, and the faces servlet has a suffix mapping, and if both use the same suffix (file extension) then getActionURL will generate a URL to the page via the faces servlet mapping, even though it is calculating that from the non faces request.

Summary

1. Use Suffix mapping for the faces servlet

2. Use an explicit mapping for the non faces servlet, using same suffix as faces mapping.

3. Proceed as per JSF spec for non faces request with faces response.

P.S. Yes, I do think there is an omission in the servlet and faces specs that means getActionURL cannot be implemented so that it always works correctly.

brucechapmana at 2007-7-16 1:56:46 > top of Java-index,Enterprise & Remote Computing,Web Tier APIs...
# 9
One more thingcom.sun.faces.INVOCATION_PATH is where the R.I.'s getActionURL caches the mapping so that it only works it out once, which is why that hack works with implementations using the R.I.'s code for getActionURL()Bruce
brucechapmana at 2007-7-16 1:56:46 > top of Java-index,Enterprise & Remote Computing,Web Tier APIs...
# 10

I went a slightly different way, and I seem to have circumvented the post-back issues described in this thread.

I wanted to put up a Servlet to accept POSTed data to initialize my JSF gui.

I started by creating/resolving a FacesContext as described at the start of this thread (using the Factories). I then use the FacesContext to retrieve a handle to the Faces managed beans so that I can update my view model. Lastly I forward to the JSF resource using the usual RequestDispatcher mechanism.

request.getRequestDispatcher("aView.jsf").forward(request, response);

Perhaps this is dumb, but it seems to work. The forward seems to initialize the paths correctly. Post-backs on the target view work correctly.

Aaron.

Aaron.Astona at 2007-7-16 1:56:46 > top of Java-index,Enterprise & Remote Computing,Web Tier APIs...