Bit of a background:
We have an existing spring webflow that we want to have ajaxified so that the page can be displayed
in a "lightbox" (at a different URL) where the user can interact with the flow in a similar way to the full existing page.
The normal registration form sits at
http://localhost:8080/csso/customer/registration?execution=e1s1.
(csso is the application name)
The webflow in Spring Webflow has an id of /customer/registration.
<flow-registry id="flowRegistry" flow-builder-services="flowBuilderServices">
<flow-location id="customer/registration" path="/WEB-INF/views/customer/registration/registration-flow.xml"/>
</flow-registry>
registration-flow.xml
<view-state id="create" model="customer" view="customer/registration/create">
<on-render>
<evaluate expression="customer.setAcceptTermsAndConditions(false)"/>
</on-render>
<transition on="submit" to="confirm" />
<transition on="cancel" to="cancel" bind="false" />
</view-state>
Now my problem is I need to access this page in two different ways.
Directly. Which works currently.
As an include in a different JSP page
(tabEntry.jsp accessed with controller /tabEntry) in such a way that the flow of the form is not interrupted.
How can I include /customer/registration inside tabEntry in such a way that clicking on submit goes through the same flow?
Problems faced:
Including /customer/registration by jsp:include doesn't work since /customer/registration is not a controller. Also #include doesn't work since a call to /customer/redirection includes a redirect which sets the execution key for webflow
The form action is automatically set to /tabEntry but it should be set to the second state of /customer/registation to continue with the flow.
Happy to accept design changes instead of hammering it technically.
Related
I have a use case where I need to redirect to an external URL via spring webflow, the url might change every time the user clicks on the link. I am trying to use externalRedirect within the flow.xml and passing in the url in a localAttributeMap and returning it with the event.
The summary.xhtml code looks something like this.
<h:commandLink action="redirect" target="_blank">
Redirect to new page
</h:commandLink>
The flow.xml is something like this:
<view-state id="summary" view="/flows/forms/summary.xhtml">
<transition on="redirect" to="retrieveUri" />
</view-state>
<action-state id="retrieveUri">
<evaluate expression="redirectAction.execute(requestContext, formContext)" />
<transition on="successRedirect" to="externalView" />
</action-state>
<view-state id="externalView" view="externalRedirect:#{currentEvent.attributes.redirectUrl}">
</view-state>
On the code base I am using I am forced to only use Actions which extend MultiAction and I can't use any other service have return types like String. My action class with the execute method which returns an Event looks something like below:
public Event execute(RequestContext context, FormContext formContext) {
LocalAttributeMap lam = new LocalAttributeMap() ;
lam.put("redirectUrl", "https://google.com") ;
//Above would of course be replaced with some service layer call but I need to redirect to this url and that too via classes extending MultiAction.
return new Event(this, " successRedirect", lam) ;
}
However this does not seem to work for me and I am getting the below errors:
Caused by: org.springframework.binding.expression.EvaluationExpression : An ElException occured getting the value for expression 'currentEvent.attributes.redirectUrl' on context [class org.springftamework.webflow.engine.impl.RequestControlContextImpl]
at org.springframework.binding.expression.spel.SpringElExpression.getValue(SpringElExpression.java:92)
at org.springframework.webflow.action.ExternalRedirectAction.doExecute(ExternalRedirectAction.java:42)
Caused by: org.springframework.expression.spel.SpelEvaluationExpression : EL1007E:(pos 13): Property or field 'attributes' cannot be found on null
at org.springframework.expression.spel.ast.PropertyOrFieldReference.readProperty(PropertyOrFieldReference.java:208)
at org.springframework.expression.spel.ast.PropertyOrFieldReference.getValueInternal(PropertyOrFieldReference.java:85)
at org.springframework.expression.spel.ast.PropertyOrFieldReference.getValueInternal(PropertyOrFieldReference.java:78)
Being new to spring webflow, I am not sure of the issue and would appreciate some help in understanding the issue and whether is the correct way of using this.
The version of spring-webflow is 2.3.0.RELEASE within the project
Background
A user must submit report parameters to the server. The server redirects the user to a URL. That URL runs Oracle Reports to produce a PDF (or web page).
The system uses a relatively slow authentication mechanism. But I'm not looking to restart the web flow, so the authentication should not otherwise interfere with the redirect.
JSP
The "Run Report" button is coded as follows:
<button type="submit" id="run" name="_eventId_run">
<fmt:message key="form.button.report.run" />
</button>
The rest of the page binds the parameters to a map in the DAO, such as:
<form:radiobutton path="parameters['service']" class="service" value="..." />
Submitting the form shows that the bind variables are set in the DAO map correctly. Further, the report is able to generate the URL used for redirection.
The form itself resembles:
<form:form modelAttribute="report" action="${flowExecutionUrl}"
method="post">
<fieldset>
<%-- secret tokens --%>
<tiles:insertAttribute name="hidden" />
<%-- includes the requested report form parameters --%>
<jsp:include page="${reportKey}.jsp" />
<%-- includes the aforementioned submit button %-->
<tiles:insertAttribute name="reportButtons" />
</fieldset>
</form:form>
Flow
The flow has three view states: list reports, enter parameters, and run report. The last two are pertinent:
<view-state id="parameters" model="report" view="flow/reports/parameters">
<transition on="run" to="redirect">
<evaluate expression="reportService.run(report)" result="flowScope.url" />
</transition>
</view-state>
<view-state id="redirect" view="externalRedirect:#{flowScope.url}"/>
The reportService.run(report) method is being called. The report parameters are being bound. The return result is, indeed, the correct URL.
Java
The report service itself is reasonably trivial:
public String run(Report report) {
Map<String, String> parameters = report.getParameters();
String url = getConfigurationValue("ReportUrl");
for (String key : parameters.keySet()) {
info("Report Parameter: {} = {}", key, parameters.get(key));
}
return url;
}
Again, the correct url is being returned. There is no controller class. The Report DAO could hardly be simpler:
public class Report extends DAOBase implements Serializable {
/** Values to pass into the report. */
private Map<String, String> parameters;
public Report() {
setParameters(createParameters());
}
// standard accessors and serial version not shown
}
Problem
It appears that the application is performing a POST-Redirect-GET (PRG). The GET does not direct the browser to the URL set by flowScope.url. (I have not verified that flowScope.url contains valid data.) When the POST operation completes, the application is redirected to the parameter flow, rather than the run button redirecting to the redirect flow.
From the schema definition, everything seems correct.
Questions
Using Spring 4.1.2, what needs to change so that the externalRedirect sends the browser to the URL returned by reportService.run(...)?
Does the form need to supply a different value for flowExecutionUrl?
Attempts
Here are some of the various changes that have been made to no avail:
Use $ in the EL, instead of # (i.e., externalRedirect:${flowScope.url})
Use <end-state id="redirect" view="externalRedirect:#{flowScope.url}"/>
Use <view-state id="redirect" view="externalRedirect:http://google.com"/>
Use form:button instead of button
Changing the redirect view-state and removing the expression evaluation causes the redirect to fire. For example:
<transition on="run" to="redirect">
<!-- <evaluate expression="reportService.run(report)" result="flowScope.url" /> -->
</transition>
...
<view-state id="redirect" view="externalRedirect:http://google.com"/>
This, of course, means that the report parameters are never used, which won't work.
Remove the evaluation from the transition:
<transition on="run" to="redirect"/>
Call the run method within an EL statement to generate the URL:
<view-state id="redirect" view="externalRedirect:#{reportService.run(report)}"/>
I am using Spring Web Flow 2.3 and I have a page that has two forms on it that transition to different places depending on which is submitted. To accomplish this, I have one composite model object for my view-state that holds the two forms inside. The problem I am seeing is that if transition A is fired, I only want to validate form A, and likewise with form B - only want to validate B if B transition fired. I am not sure how to indicate which form to validate. View state that is validating the entire compositeForm for each transition:
<view-state model="compositeForm">
<transition on="formAsubmit" to="formApage" validate="true"/>
<transition on="formBsubmit" to="formBpage" validate="true"/>
</view-state>
Does anyone know how I can trigger a custom validator to validate differently depending on which transition was fired?
Thanks for you help.
Steve
I don't know about a custom validator for each, but within your validation method, I think you could use the RequestContextHolder.getRequestContext() to getCurrentTransition() or getCurrentEvent() and compare manually to the getId() value.
What I ended up doing was to manually trigger my validation when form B was submitted and transition to a decision-state that checks if there were validation errors. It's a little ugly, but I feel like it's the best way:
<view-state id="start" model="compositeForm">
<transition on="formAsubmit" to="pageA" validate="true"/>
<transition on="formBsubmit" to="isFormBValid" validate="false">
<evaluate expression="formBValidator.validate(compositeForm.formB, messageContext)"/>
</transition
</view-state>
<decision-state id="isFormBValid">
<if test="messageContext.hasErrorMessages()" then="start" else="pageB"/>
</decision-state>
This is not the best solution, but at least is solves the problem. This is how I obtained my transition id and view-state id in vlaidator.
Transition id
RequestContextHolder.getRequestContext().getFlowExecutionContext().getActiveSession().getState().getId();
view-state id
RequestContextHolder.getRequestContext().getFlowExecutionContext().getActiveSession().getState().getId();
I´m new to Spring Webflow, so I have a Question about a (or more) Flows.
I want to build a few facelets in JSF and one start Page that can have different ui-params in an ui-include, depending on what i want to add in the flow later.
Example application.xhtml:
`<ui:include src="start.xhtml">
<ui:param name="page1" value="page1.xhtml" />
<ui:param name="page2" value="page2.xhtml" />
<!-- page 3 should be ignored -->
<!-- <ui:param name="page3" value="page3.xhtml" /> -->
<ui:param name="page4" value="page4.xhtml" />
</ui:include>`
Now i have my start-flow.xml where i want to check, which ui:params the page got.
But i don´t knwo how to to that, and i couldn´t find anything similar on the web. so i assume, this might be the wrong way to do so :-)
Can anyone help me out?
My goal is to have a flow (independent from hardcoded facelets, so i can check a list of ui:params what facelets i have and to use them, like:
`<view-state id="start" view="${flowScope.allViews[0]}">
<!-- assuming every facelet has a next-action -->
<transition on="next" to="${flowScope.allViews[1]}" />
</view-state>`
Pretty sure you cant just get a list of JSF includes/params in Spring Webflow.
The closest thing you can get to it is to grab FacesContext and try to locate known components by their ids:
boolean haveComponent1 = (FacesContext.getCurrentInstance().getViewRoot().findComponent("component1") != null);
Assuming you know component IDs in included pages in webflow you can do something like:
<decision-state id="doSomSink">
<on-entry>
<evaluate expression='FacesContext.getCurrentInstance().getViewRoot().findComponent("component1") != null)' result="flowScope.haveComponent1" result-type="bool"></evaluate>
</on-entry>
<if test="flowScope.haveComponent1" then="doIt" else="doNothing"/>
</decision-state>
I am using Spring 3.x and WebFlow 2.0. I am trying to submit the form with a new event id.
<view-state id="projectSearch" view="project.projectSearch" model="searchCommand">
<transition on="search" to="projectSearch" bind="true" >
<evaluate expression="formAction.findProjects(flowRequestContext, searchCommand)"/>
</transition>
</view-state>
and
public Event findProjects(RequestContext context, SearchCommand command) throws Exception
{
return success();
}
On form submit I set the event key to search. With above configuration I do not receive any request to findProject() method. But if I remove model="" attribute from view-state I get the request received by the method. In this case I do not get form values.
Can anybody please direct me the correct path.
Thanks,
Vishal
It appeared to be jQuery for submit was causing the problem. If I comment the jQuery submit it works. I changed few lines and removed javascript need for form submit.