PDA

View Full Version : JSF and WebFlow



bura
Apr 20th, 2005, 05:38 AM
Hi,

Does anybody have already some experiences or ideas about using WebFlow together with JSF? We'd like to use WebFlow to describe the flow whereas the pages should be built with JSF components.

Andi

timoCino
Apr 20th, 2005, 07:04 AM
Hi all,

i am also more than interested in the integration of webflow into jsf. Heard that there should be an integration lib as work in progress???

Keith Donald
Apr 20th, 2005, 08:29 AM
JSF integration is a high priority for us, definitely.

Neither Erwin or I are JSF experts, so we're certainly looking to work with some experts as part of this -- just like we did with Jose and Caesar on the Portlet integration.

Keith

Gideon
Apr 20th, 2005, 12:50 PM
Hi

what you think will be the benefit of integrating JSF into WebFlow? the only difference i think is that JSF are bundled with TagLibs but there are many TagLibs that do the same.

or do you mean webflow support for JSF?

thanks.

mfg Gideon

klr8
Apr 21st, 2005, 02:25 AM
JSF could be just another (component based) view technology. SWF can take care of the 'flow' responsabilities: what should happen when a button is clicked? Where (what page) do we need to go? ...

So "integrating" just means: make it easy to use JSF as a view technology with SWF.

Erwin

bura
Apr 21st, 2005, 02:30 AM
In some cases, I'd like to substitute the faces' flow control by web flow but still use JSF's component oriented web interface design mechanism. JSF itself doesn't know the notion of start and stop states, and therefore it can not verify that a flow starts at the correct state. Additionally it doesn't handle multiple submits or browsers back, forward and refresh buttons. Web flow seems to address all this limitations.

On the other side, web flow dictates the flows' execution order in all cases, making it inappropriate to be used to describe general transitions between views on a "free flow" basis. IMO JSF flow and web flow complement each other providing means to express free flows as well as strictly guided flows.

What are your feelings about that?
or do you know any extensions to JSF that allow to define strictly guided flows as this is done in spring's web flow?

Thanks,
Andi

klr8
Apr 21st, 2005, 02:43 AM
Andi,

Your explanation makes al lot of sense to me. I don't know much about JSF (never used it IRL) so I can't really comment on it, but the way you explain it seems they complement each other very well indeed.

Erwin

Gideon
Apr 21st, 2005, 07:42 AM
Hi,

i have never used JSF, but looking into some books and on some Spring JSF examples. JSF looks onyle like a view technology with supports:

rendering forms with good taglib support;
validate forms;
create a forms/site navigation.

but by the way, jstl with spring taglib and display taglib etc. can do the same without having any configuration problems etc.

so where is the benfit of using JSF (the display table is muc betten then jsf table etc)

thanks

mfg Gideon

bura
Apr 21st, 2005, 08:16 AM
Hi Gideon

JSF web interfaces are built by components. The idea is to compose pages just by dragging components from a palette to a web site (comparable to ASP.NET or VisualBasic GUI creation). Further, it standardises verification and conversion of input data, again using components. So JSF's strength is its component model (e.g. the synchronizer token can be implemented in a component and placed on pages where it's needed. You don't need to verify explicitly the tokens in your actions. This is done transparently by the component.).

But JSF's navigation model is limited in the sense that it doesn't address all the problems encountered in typical form series (wizards, see above). Web flow could do a great job here. Web Flow on the other side supports strictly guided flows only. A good JSF-web flow integration should provide both, JSF flows to do standard navigation and web flow for forms spanning over multiple pages.

btw: another JSF problem is the lack of good components and their abstraction in general. But both can be solved by writing new components.

Andi

Gideon
Apr 21st, 2005, 08:30 AM
Hi,

ok i have read about this components, but at time for me the components are only a taglib. so the display taglibs are components too.

or do you mean that jsf is a taglib for a so called "visual studio" where you dont have to implement tha tags, because you can use drag and drop?

if it so, then i never use a gui (or something like Visual Studio) to code JSP JSF or simple HTML pages and so JSF have no benetif for me is this right?

mfg Gideon

garpinc2
Apr 21st, 2005, 05:26 PM
I suggest:

http://opensource.atlassian.com/confluence/spring/display/JSF/Home

I think first of all there are ideas here specifically regarding bean implementing actionlistener.

Based on that article do you guys have any ideas on integration?

I think the most simple place to integrate would be in classes like JstlView

i.e:
JSFView, JSFJstlView, JSFTilesView and JSFJstlTilesView.

that then you could apply as the view resolver view class.


The challenge here I think is keeping the faces component tree in sync as you go through the flow and somehow hooking into web flow xml to resolve JSF actions rather than JSF built in navigation.

Basically as I understand it when going to a page the component tree would be created by FacesServlet in web.xml. I think that should be instead created by the view and stored in the session somehow delegating to the JSF classes that do it.

But I am no expert.... I think contacting the people on that page should get the integration off the ground.


Any ideas based on the article?

garpinc2
Apr 21st, 2005, 05:28 PM
Of course it would be great if this works with portlets too. :-)

garpinc2
Apr 21st, 2005, 08:38 PM
Another very useful snippet of information. It appears to simulate FacesContext setup:

http://www.thoughtsabout.net/blog/archives/000033.html

garpinc2
Apr 21st, 2005, 11:11 PM
http://www.jroller.com/comments/why/Weblog/how_to_do_a_jsf

garpinc2
Apr 22nd, 2005, 02:53 PM
I am running into a problem implementing JsfPortletView

Line here is failing with class cast.


facesContext = contextFactory.getFacesContext(portletContext, request, response, lifecycle);

I think this is occurring because
contextFactory is a portlet context factory because of jsf-portlet integration library.

request and response are not portlet compatible and hence class cast.

So either we've got to figure out a way to continue to use servlet context factory here or somehow convert servlet request and response into portlet request and response.

That's my 2cents... Any ideas?


Here is the code so far..


/*

* Created on Apr 22, 2005

*

* TODO To change the template for this generated file go to

* Window - Preferences - Java - Code Style - Code Templates

*/

package org.springframework.web.servlet.view;

import java.util.Map;

import javax.faces.FactoryFinder;

import javax.faces.component.UIViewRoot;

import javax.faces.context.FacesContext;

import javax.faces.context.FacesContextFactory;

import javax.faces.lifecycle.Lifecycle;

import javax.faces.lifecycle.LifecycleFactory;

import javax.portlet.PortletContext;

import javax.servlet.RequestDispatcher;

import javax.servlet.ServletException;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import org.springframework.web.portlet.context.PortletApp licationContext;





/**

* @author jztb88

*

* TODO To change the template for this generated type comment go to

* Window - Preferences - Java - Code Style - Code Templates

*/

public class JsfPortletView extends InternalResourceView {

/**

* Render the internal resource given the specified model.

* This includes setting the model as request attributes.

*/

protected void renderMergedOutputModel(

Map model, HttpServletRequest request, HttpServletResponse response) throws Exception {

// Expose the model object as request attributes.

exposeModelAsRequestAttributes(model, request);

// Expose helpers as request attributes, if any.

exposeHelpers(request);

FacesContext facesContext = getFacesContext(request, response);

// you could just render the page here but since we are still in filter, it might be

// a better idea to forward to the actual page

// facesContext.getApplication().getViewHandler().ren derView(facesContext, facesContext.getViewRoot() );

// Forward to the resource (typically a JSP).

// Note: The JSP is supposed to determine the content type itself.

RequestDispatcher rd = request.getRequestDispatcher(facesContext.getViewR oot().getViewId() );

if (rd == null) {

throw new ServletException(

"Could not get RequestDispatcher for [" + getUrl() + "]: check that this file exists within your WAR");

}

// If already included or response already committed, perform include, else forward.

if (useInclude(request, response)) {

rd.include(request, response);

if (logger.isDebugEnabled()) {

logger.debug("Included resource [" + getUrl() + "] in InternalResourceView '" + getBeanName() + "'");

}

}

else {

rd.forward(request, response);

if (logger.isDebugEnabled()) {

logger.debug("Forwarded to resource [" + getUrl() + "] in InternalResourceView '" + getBeanName() + "'");

}

}

}

// You need an inner class to be able to call FacesContext.setCurrentInstance

// since it's a protected method

private abstract static class InnerFacesContext extends FacesContext

{

protected static void setFacesContextAsCurrentInstance(FacesContext facesContext) {

FacesContext.setCurrentInstance(facesContext);

}

}

private FacesContext getFacesContext(HttpServletRequest request, HttpServletResponse response) throws Exception {

// Try to get it first

FacesContext facesContext = FacesContext.getCurrentInstance();

if (facesContext != null) return facesContext;

FacesContextFactory contextFactory = (FacesContextFactory)FactoryFinder.getFactory(Fact oryFinder.FACES_CONTEXT_FACTORY);

LifecycleFactory lifecycleFactory = (LifecycleFactory)FactoryFinder.getFactory(Factory Finder.LIFECYCLE_FACTORY);

Lifecycle lifecycle = lifecycleFactory.getLifecycle(LifecycleFactory.DEF AULT_LIFECYCLE);

PortletContext portletContext = ((PortletApplicationContext) getApplicationContext()).getPortletContext();

// Doesn't set this instance as the current instance of FacesContext.getCurrentInstance

facesContext = contextFactory.getFacesContext(portletContext, request, response, lifecycle);

// Set using our inner class

InnerFacesContext.setFacesContextAsCurrentInstance (facesContext);


// Determine the path for the request dispatcher.

String dispatcherPath = prepareForRendering(request, response);

// set a new viewRoot, otherwise context.getViewRoot returns null

UIViewRoot view = facesContext.getApplication().getViewHandler().cre ateView(facesContext, dispatcherPath);

facesContext.setViewRoot(view);

return facesContext;

}


}

garpinc2
Apr 22nd, 2005, 03:31 PM
I removed jsf integration library and went with Servlet context all the way and it works.... At least I was able to do.


<%@ taglib uri="http&#58;//java.sun.com/jsf/html" prefix="h" %>
<%@ taglib uri="http&#58;//java.sun.com/jsf/core" prefix="f" %>
<f&#58;loadBundle basename="ApplicationResources" var="Message"/>

<f&#58;view>
<h&#58;outputText value="#&#123;Message&#91;'contactus.title'&#93;&#125;"/>
</f&#58;view>

I'll have to test more and I am looking for input as to if facescontext and servletcontext are behaving properly. I would think that it should be saving facescontext in portletcontext not servletcontext????


/*
* Created on Apr 22, 2005
*
* TODO To change the template for this generated file go to
* Window - Preferences - Java - Code Style - Code Templates
*/
package org.springframework.web.servlet.view;

import java.util.Map;

import javax.faces.FactoryFinder;
import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;
import javax.faces.context.FacesContextFactory;
import javax.faces.lifecycle.Lifecycle;
import javax.faces.lifecycle.LifecycleFactory;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;



/**
* @author jztb88
*
* TODO To change the template for this generated type comment go to
* Window - Preferences - Java - Code Style - Code Templates
*/
public class JsfPortletView extends InternalResourceView &#123;

/**
* Render the internal resource given the specified model.
* This includes setting the model as request attributes.
*/
protected void renderMergedOutputModel&#40;
Map model, HttpServletRequest request, HttpServletResponse response&#41; throws Exception &#123;

// Expose the model object as request attributes.
exposeModelAsRequestAttributes&#40;model, request&#41;;

// Expose helpers as request attributes, if any.
exposeHelpers&#40;request&#41;;

FacesContext facesContext = getFacesContext&#40;request, response&#41;;

// you could just render the page here but since we are still in filter, it might be
// a better idea to forward to the actual page
// facesContext.getApplication&#40;&#41;.getViewHandler&#40;&#41;.ren derView&#40;facesContext, facesContext.getViewRoot&#40;&#41; &#41;;

// Forward to the resource &#40;typically a JSP&#41;.
// Note&#58; The JSP is supposed to determine the content type itself.
RequestDispatcher rd = request.getRequestDispatcher&#40;facesContext.getViewR oot&#40;&#41;.getViewId&#40;&#41; &#41;;

if &#40;rd == null&#41; &#123;
throw new ServletException&#40;
"Could not get RequestDispatcher for &#91;" + getUrl&#40;&#41; + "&#93;&#58; check that this file exists within your WAR"&#41;;
&#125;

// If already included or response already committed, perform include, else forward.
if &#40;useInclude&#40;request, response&#41;&#41; &#123;
rd.include&#40;request, response&#41;;
if &#40;logger.isDebugEnabled&#40;&#41;&#41; &#123;
logger.debug&#40;"Included resource &#91;" + getUrl&#40;&#41; + "&#93; in InternalResourceView '" + getBeanName&#40;&#41; + "'"&#41;;
&#125;
&#125;
else &#123;
rd.forward&#40;request, response&#41;;
if &#40;logger.isDebugEnabled&#40;&#41;&#41; &#123;
logger.debug&#40;"Forwarded to resource &#91;" + getUrl&#40;&#41; + "&#93; in InternalResourceView '" + getBeanName&#40;&#41; + "'"&#41;;
&#125;
&#125;
&#125;
// You need an inner class to be able to call FacesContext.setCurrentInstance
// since it's a protected method
private abstract static class InnerFacesContext extends FacesContext
&#123;
protected static void setFacesContextAsCurrentInstance&#40;FacesContext facesContext&#41; &#123;
FacesContext.setCurrentInstance&#40;facesContext&#41;;
&#125;
&#125;

private FacesContext getFacesContext&#40;HttpServletRequest request, HttpServletResponse response&#41; throws Exception &#123;
// Try to get it first
FacesContext facesContext = FacesContext.getCurrentInstance&#40;&#41;;
if &#40;facesContext != null&#41; return facesContext;

FacesContextFactory contextFactory = &#40;FacesContextFactory&#41;FactoryFinder.getFactory&#40;Fact oryFinder.FACES_CONTEXT_FACTORY&#41;;
LifecycleFactory lifecycleFactory = &#40;LifecycleFactory&#41;FactoryFinder.getFactory&#40;Factory Finder.LIFECYCLE_FACTORY&#41;;
Lifecycle lifecycle = lifecycleFactory.getLifecycle&#40;LifecycleFactory.DEF AULT_LIFECYCLE&#41;;

//PortletContext portletContext = &#40;&#40;PortletApplicationContext&#41; getApplicationContext&#40;&#41;&#41;.getPortletContext&#40;&#41;;
// Doesn't set this instance as the current instance of FacesContext.getCurrentInstance
facesContext = contextFactory.getFacesContext&#40;request.getSession&#40; &#41;.getServletContext&#40;&#41;, request, response, lifecycle&#41;;

// Set using our inner class
InnerFacesContext.setFacesContextAsCurrentInstance &#40;facesContext&#41;;

// Determine the path for the request dispatcher.
String dispatcherPath = prepareForRendering&#40;request, response&#41;;

// set a new viewRoot, otherwise context.getViewRoot returns null
UIViewRoot view = facesContext.getApplication&#40;&#41;.getViewHandler&#40;&#41;.cre ateView&#40;facesContext, dispatcherPath&#41;;
facesContext.setViewRoot&#40;view&#41;;

return facesContext;
&#125;



&#125;

garpinc2
Apr 22nd, 2005, 03:37 PM
Of course now I don't need a portlet specific jsf view...

garpinc2
Apr 22nd, 2005, 04:21 PM
I tried hello world application from here:

http://www.exadel.com/tutorial/jsf/jsftutorial-kickstart.html

It displayed correctly but looking at soure it seems it set the form action to be the jsp page rather than <portlet:actionURL/>

looking for input here. I don't know if integration library would have handled this or if there is a way to do this in the bridge... Let me know.

eruiz
Apr 23rd, 2005, 01:19 AM
Hi,

Apache MyFaces (http://myfaces.apache.org/) is a Open Source Implementation of the JavaServer Faces Framework. It has support for Portlet JSR-168.

I believe that an interesting way to support JSF could be to integrate MyFaces in Spring MVC.

garpinc2
Apr 23rd, 2005, 02:02 PM
As you said MyFaces is just an implementation. I don't see why it would be any easier than integrating Sun's JSF. The last thing I was trying to understand is about flowId. It seems that a component tree is saved in context for each view in JSF application. In the standard way this view is associated with the name of a jsp. I think with webflow it would be instead some combination of flowId and viewId. That way if you ever navigate to the same flow-view combination JSF can locate the view component tree. Does that sound right? I wish a JSF expert would join this thread...

craigmcc
Apr 24th, 2005, 07:26 PM
JSF integration is a high priority for us, definitely.

Neither Erwin or I are JSF experts, so we're certainly looking to work with some experts as part of this -- just like we did with Jose and Caesar on the Portlet integration.

Keith

A colleague pointed me at this thread, and it is of definite interest to me. I would also hope that folks here consider me a "JSF Expert" :D

When I was at TSSJS in Las Vegas last month, I met with Keith and several of the other Spring developers, and saw a demo of SWF during one of the sessions. I was definitely intrigued, because (in parallel) I had been working on a next generation web application framework that (unlike nearly all current ones) is built on top off JSF, instead of being agnostic to it. That work has been accepted as a Struts subproject called "Shale" -- see the following Wiki page for details:

http://wiki.apache.org/struts/StrutsShale

One of the features on my "must have" list for Shale was good support for what I had called "dialogs" in the initial proposal, and I had done a fairly primitive implementation of dialog management (really, focused more on the problem of saving and restoring state across HTTP requests than on the state transition problem).

Once I saw the SWF demo, and how elegant it was to put flow definitions together, it became clear that this was a much better architecture -- and the queston was what to do next.

At a strategic level, there are two approaches I can see for how you could integrate SWF and JSF together:

* Use JSF simply as a view technology (just forward
to "/foo.faces" instead of "/foo.jsp" and you'll enable
the JSF component rendering capabilities.

* Integrate with JSF's navigation management (at least
when you are within a flow), naturally using JSF pages
to do the actual rendering (as above), but also leveraging
the ability of a JSF action handler (the method that gets
invoked when, say, a submit button is pressed) to return
a logical outcome that could drive the state transition
mechanism of SWF, instead of driving normal page
navigation.

To explore these ideas further, I've implemented a very simple version of what SWF in Shale, in the "org.apache.shale.dialog" package (the package javadocs for this package in the core library has background info). This would be sufficient if your needs are pretty simple, or if you did not choose to integrate Spring into your Shale based applicaton (athough that works pretty nicely as well -- I use the managed beans integration of Spring 1.1.5 or later in my demo app).

What seems needed in the future, however, is a full blown integration of SWF and JSF that accomplishes both approaches described above. It also seems to make sense, as you've done with other integrations, that the code for this actually live in Spring (where, of course, any Shale user who wants to leverage it can get it for free as an alternative to the simple dialog support already present).

If the SWF developers are interested, I volunteer to help create such an integration layer, and will contribute it to the Spring codebase. If that is of interest, it would probably be appropriate to conduct the detailed design discussions on the Spring developer mailing list, right?

Craig McClanahan

Keith Donald
Apr 25th, 2005, 12:18 AM
Craig,

Great to hear from you! Yes, I agree first-class SWF/JSF integration is what we need, and we're keen to host it and work with you on developing the integration. Yes, the dev list is the best place for such discussions.

You outline here sounds like a great start. Perhaps we can take one of our existing SWF sample apps and build out the JSF integration that way--in our samples/webflow directory in CVS are 5 different sample apps that each illustrate different features of SWF. Phonebook already demonstrates Portlet integration, Birthdate demos Struts integration--we could tack JSF on top of one of those or another, for example "sell item" (a wizard flow.) Or we could create a new app just to showcase JSF+SWF.

How do you see Shale fitting into the JSF+SWF equation. Will you need Shale to use SWF+JSF? When would you use all 3?

When I get back from onsite (next week) I'd be in a good position to review an initial stab at integration and start diving deeper into JSF. Between now and then I'll certainly have the evenings to participate in some design discusions on the dev lists.

Cheers,

Keith

klr8
Apr 25th, 2005, 05:57 AM
Excellent!

Really looking forward to JSF - SWF integration. That way I'll finally have to take a deeper look at JSF! :-)

Anyway, the second approach Craig suggest below seems to be the correct course of action.

Erwin

bura
Apr 25th, 2005, 07:36 AM
Wow, nice to have you both here ;-)

In the meantime, we have designed a possible solution: in contrast to garpinc2's solution that integrates JSF into spring using a special view extension, our solution builds on the extensible and adaptive JSF model.
We envision a web application as a JSF application: pages linked with links between them. To introduce within the JSF application a more structured webflow, we propose to put a special page within the JSF application. Links are possible to this page and from this page, nicyly interconnecting the JSF and the webflow worlds.
We suggest to use a special JSF navigation handler that delegates all SWF specific tasks to a web flow execution manager.
Using JSF as the top-level component and web flow as a part of the controller logic has several benefits. First one can use the whole range of features provided by JSF (i.e. components, method binding, value binding just to mention the basic ones). Second it's possible to describe unguided transitions between pages using the simple JSF navigation rules. Third, it's possible to combine JSF actions and web flow action states together.

JSF pages are considered to be created using graphical editors. A JSF web flow integration should allow reusing such pages and providing the possibility to combine the two flow architectures.

Delegating the control to the right part (JSF or web flow) can be separated in three phases:
* The JSF view is rendered for the first time, i.e. the flow starts
* The flow is executed
* The flow is terminated in this request-response iteration.

To solve the first one and still to be conformant to JSF rules, one could save web flow views at a separate location. Using a prefix for transition action strings or, as already mentioned above, generating some dummy pages would work as well (in general, we just need some kind of flag).

The second case is recognized by the three hidden fields that are embedded into HTML and used by web flow to save the flow's state.

The last case can be recognized using a FlowExecutionListener (since the three fields are transmitted in the last request, navigation control is delegated to web flow). After returning from the FlowExecutionManager JSF's original navigation handler will take over control and the appropriate view will be displayed (i.e. a flow has to end with an action state).

In principle, this is exactly what Craig has proposed.

Adding a web flow component to JSF would save to add the hidden input fields explicitly.

We already have a prototype proofing the navigation handler approach.

Andi

garpinc2
Apr 25th, 2005, 09:54 AM
If the solution is not implemented by extending view will the spring features of implementing the view in whatever technology you want be still true? I'm looking forward to hear Craig's implentation ideas...

garpinc2
Apr 25th, 2005, 10:05 AM
Wouldn't an irc channel be more efficient than dev list? In a way that discussions could be archived on the site for community review?

bura
Apr 25th, 2005, 10:24 AM
No, not really. This problem has to be solved by filters or clever mappings (any better ideas?). Our approach assumes JSF as standard view technology (Although, the only thing you need are the three hidden fields that are embedded in the markup. This way it should be possible to access a flow through different view technologies. It should be even possible to have different view techs within a single flow. However, we consider this as unimportant. But different flows built with different view technologies is indeed an interesting point.)

counter questions
*How would you allow to use JSF flows?
*How can both flow models be integrated in one web application?
*Does it support the use of JSF features (i.e. bindings, validators, converters)?
*Can you still use available JSF/web flow design tools?
*Beside the integration of different view technologies, do you see any important advantages that favor your approach over the other?

Andi

garpinc2
Apr 25th, 2005, 11:39 AM
counter questions

*How would you allow to use JSF flows?
I thought the extended view idea was the best way because it fit into the current architecture. I never liked the idea of the Faces servlet because it doesn't give any leverage to decide what to do based on the request. Using the spring controler to proxy to jsf view gives spring full control of what to do as a result of the request either proxying to faces stuff or any other technology. In the meantime spring interceptors can be applied. This is particularly important with portlet development since filters are not yet supported.


*How can both flow models be integrated in one web application?

The view resolvers handle this not the flow as far as I understand.

According to doc "When mixing different view technologies in a web application, you can use the ResourceBundleViewResolver" the alternative method is "Spring supports more than just one view resolver"


*Does it support the use of JSF features (i.e. bindings, validators, converters)?

My thought was if the view is a faces implementation it must have access to facesContext somehow. If the post is back to the same view with the component then I don't see why all those features wouldn't be supported.



*Can you still use available JSF/web flow design tools?

Nothing would have changed wrt architecture so I also don't see why that would change.


*Beside the integration of different view technologies, do you see any important advantages that favor your approach over the other?

I wouldn't consider me the expert here. I'm glad we've got some real experts involved. I'm not proposing any solution, I just realized I had a need and tried to get some idea what was involved. I think between Keith and Craig a far superior solution will be developed than I could have done. I just hope my business need to support JSF in portlets with this architecture will be realized.

rector
May 3rd, 2005, 08:51 PM
I did not want to join Spring forum until my own project supports Spring, but seeing this discussion related to JSF, I could not help posting a message.

Thinking about dialogs, I think of them from a desktop programmer point of view. In a desktop OS dialog is a window with one or several panels. If it is a notebook, than a user can select a page explicitly, if it is a simple dialog, then panels can be defined in a resource, and switched from the code simply by selecting visible panel ID.

Thus, a dialog is is not a usual flow of pages. I think, a dialog is an object which can have state and can visulalise itself depending on the state. Think portlets.

Trying to integrate my own component into JSF, I used the idea of stateful visual component, along with David Geary's tip of how to compose JSF page from subpages. So, I have "master" page ("dialog window frame") which includes "panels":



<html>
<%@ taglib uri="http&#58;//java.sun.com/jsf/html" prefix="h" %>
<%@ taglib uri="http&#58;//java.sun.com/jsf/core" prefix="f" %>
<f&#58;view>
<jsp&#58;include page="step1.jsp" />
<jsp&#58;include page="step2.jsp" />
<jsp&#58;include page="step3.jsp" />
</f&#58;view>
</html>



To introduce within the JSF application a more structured webflow, we propose to put a special page within the JSF application. Links are possible to this page and from this page, nicyly interconnecting the JSF and the webflow worlds.

Yep, kinda like this. The master page is the only location where client sends requests (for that dialog, or for the flow if you wish). Client either submits input to master page using POST, or loads master page using GET. In the latter case master page builds itself and shows the panel that corresponds to current state. Each panel looks something like this:



<html>
<%@ taglib uri="http&#58;//java.sun.com/jsf/html" prefix="h" %>
<%@ taglib uri="http&#58;//java.sun.com/jsf/core" prefix="f" %>
<f&#58;subview id="step1" rendered="#&#123;wizardBean.step1&#125;">
<h&#58;form id="page_step1" >
<h&#58;messages />
<h&#58;panelGrid columns="2" border="1" cellspacing="0">

<h&#58;outputText value="User Name&#58;"/>
<h&#58;inputText id="userNameEdit" value="#&#123;wizardBean.userName&#125;"/>

<h&#58;outputText value="Password&#58;"/>
<h&#58;inputText id="userPasswordEdit" value="#&#123;wizardBean.userPassword&#125;"/>

<h&#58;commandButton action="#&#123;wizardBean.cancel&#125;" value="Cancel"/>
<h&#58;commandButton action="#&#123;wizardBean.Next&#125;" value="Next"/>

</h&#58;panelGrid>
</h&#58;form>
</f&#58;subview>
</jsp&#58;root>


The important thing about code above is "rendered" attribute. Maybe I did not look well enough, but I could not create a full-blown expression like this rendered="#{wizardBean.currentStep.equals(1)} So, I am using a bunch of boolean methods, as many as many panels. Kinda ugly, but it works. Notice, that each included JSP contains subview.

The backing bean is simple and is not interesting, so it is not shown here. The faces-config.xml is actually more important:



<navigation-rule>
<from-view-id>/signup.jsp</from-view-id>
<navigation-case>
<from-outcome>reload</from-outcome>
<to-view-id>/signup.jsp</to-view-id>
redirect/>
</navigation-case>
</navigation-rule>

<managed-bean>
<description>
Signup Wizard bean
</description>
<managed-bean-name>wizardBean</managed-bean-name>
<managed-bean-class>com.superinterface.loginwizard.jsf.WizardBean</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
</managed-bean>


The navigation rule above along with composition of main page provides the functionality, that makes this dialog a control. This a very important concept. For a control, flow does not go from one page to another. Instead, the control is addressed by a single URL, and generates different pages ("panels") depending on its current state. Again, think portlets. Portlet is a control, dialog is a control, wizard is a multi-panel control.

First you navigate to master page signup.jsp (using GET), it includes subviews. Each subview checks the current dialog state. If state corresponds to a subview, the page it is included into master page. Then the master page is shown, now it contains panel which is synchronized with dialog state. When you click a button, an event triggers the method in the backing bean.

Backing bean updates the model and changes state if needed. Then it returns back, and JSF executes navigation rule. What it does? It goes back to itself, to the same master page. Then JSF loads master page again. State was changed, so a different subview is included according to "rendered" rule.

[JSF] doesn't handle multiple submits or browsers back, forward and refresh buttons.
Notice <redirect/> tag in the navigation rule above, this helps to fight with POSTDATA problems and provides clean refresh. Current JSF version does not have a replacement for <controller nocache="true"/> that Struts has, but I was promised that non-cachable pages will be possible in the next version ;-)

This is basically it. The only part left is to define state transitions and to create panels.

Keith Donald
May 3rd, 2005, 09:17 PM
comparing features and approaches is ok :-)

garpinc2
Apr 14th, 2006, 12:57 PM
craigmcc did you integrate JSF in a way that supports JSF portlets? If so can you point me to documentation on that?

garpinc2
Apr 18th, 2006, 10:20 AM
http://opensource.atlassian.com/projects/spring/browse/SWF-113

garpinc2
Apr 24th, 2006, 09:59 AM
I still haven't got any feedback on this. Please take 5mins to give me a tidbit of info.

Where in the framework would be the best place to initiate a flow. Currently the initial page is not part of the flow it is specified by portal integration framework and from that the flow is launched. I think it makes more sense to define the flowId of the flow to be launched and it could then pick up the page and any initial actions from flow definition.

garpinc2
Apr 27th, 2006, 09:46 AM
I have streamlined some of the JSF integration so now everything is driven by the flow including the initial state.
Below are the 3 relavent files.

portlet.xml contains the standard DispatcherPortlet portlet we know and love.
In sellItemPortlet.xml for view mode it delegates to a JsfPortletFlowController with the flowId defining the flow (in this case sellItem)
The entry state of the portlet occurs when the portlet is displayed in the portal
In this case the entry-actions for sellItem run which populate request prior to display of view-state.
On submit sellItemDetails view state is entered.

It seems to all work but now I need the spring team to get involved but for some reason (probably JavaOne) no one is responding to these JSF-Portlet-Webflow posts.


portlet.xml
<?xml version="1.0" encoding="UTF-8"?>
<portlet-app version="1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd"
xmlns="http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd">

<portlet>
<portlet-name>ReviewClaimsPortlet</portlet-name>
<portlet-class>org.springframework.web.portlet.DispatcherPortlet</portlet-class>
<init-param>
<name>contextConfigLocation</name>
<value>
classpath:spring/sellItemsPortlet.xml
</value>
</init-param>
<supports>
<mime-type>text/html</mime-type>
<portlet-mode>VIEW</portlet-mode>
</supports>
<portlet-info>
<title>Sell Items</title>
</portlet-info>
</portlet>
</portlet-app>


classpath:spring/sellItemsPortlet.xml
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">

<!-- Application context definition -->

<beans>

<bean id="portletModeControllerMapping"
class="org.springframework.web.portlet.handler.PortletMod eHandlerMapping">
<property name="portletModeMap">
<map>
<entry key="view">
<ref bean="sellItemsFlowController" />
</entry>
</map>
</property>
</bean>

<bean id="sellItemsFlowController" class="org.springframework.webflow.executor.mvc.JsfPortle tFlowController">
<property name="flowLocator" ref="flowLocator"/>
<property name="defaultFlowId"><value>sellItems</value></property>
</bean>
</beans>

sellItems.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE flow PUBLIC "-//SPRING//DTD WEBFLOW 1.0//EN"
"http://www.springframework.org/dtd/spring-webflow-1.0.dtd">

<flow start-state="sellItems">

<view-state id="sellItems" view="/WEB-INF/jsp/sellItems.jsp">
<transition on="submit" to="sellItemDetail"/>
</view-state>

<view-state id="claimsDetail" view="/WEB-INF/jsp/sellItemDetail.jsp">
<transition on="submit" to="sellItems"/>
</view-state>

</flow>