Global Exception handing with Spring WebFlow Flow - java

Global Exception handing with Spring WebFlow Flow.
I am working in a Spring webflow project and I would like to know if anyone knows how to add sometype of global exception handing in the flow so if any of my java calls throw a example it will catch in and bring in to the same error system for the full system?
My old flow worked with out a issues:
<var name="member" class="xxxx" />
<decision-state id="checkIsInPending">
<if test="flowControllerActions.isInPending(member)" then="endStateMemberPending" else="name" />
</decision-state>
once I added the global to it, I started to get a error.
<var name="member" class="xxxxx" />
<global-transitions>
<transition on-exception="java.lang.Exception"
to="SystemException" />
</global-transitions>
<decision-state id="checkIsInPending">
<if test="flowControllerActions.isInPending(member)" then="endStateMemberPending" else="name" />
</decision-state>
Here is my error:
org.xml.sax.SAXParseException; lineNumber: 29; columnNumber: 40; cvc-complex-type.2.4.a: Invalid content was found starting with element 'decision-state'. One of '{"http://www.springframework.org/schema/webflow":on-end, "http://www.springframework.org/schema/webflow":output, "http://www.springframework.org/schema/webflow":exception-handler, "http://www.springframework.org/schema/webflow":bean-import}' is expected.

Try to rearrange XML tags. You are getting this exception because your flow XML does not comply with XSD. Myabe just put global-transition in the end

You can use global-transitions to catch exceptions:
<global-transitions>
<transition on-exception="example.MyBusinessException" to="state3"/>
</global-transitions>
For more details see : the documentation

Related

How to handle Exceptions with a Spring Integration Flow

I am trying to do the following using spring integration.
I would like to read from an input channel an incoming Message that goes throws a series of Steps and produces a final message.Each step can throw an Exception. This could be either due to a DB call or ExternalCall etc. Each step is considered as a service-activator. If all is well a successful a final Message is generated.I need to handle each Exception thrown by each Step as a separate case and then produce the appropriate final Message.
Using the below xml I have written a test code that passes when sucessfull but when an error occurs although I see a Final message generated in the postSend (sent=true) on channel 'bean 'finalChannel' log but the process does not return to the final queue.
Why does this happen?
<?xml version="1.0" encoding="UTF-8"?>
<context:component-scan base-package="com.demo.">
</context:component-scan>
<header-enricher input-channel="inputChannel"
output-channel="enrichedChannel">
<header name="initialMessage" expression="getPayload()" />
</header-enricher>
<!-- first-step -->
<service-activator input-channel="enrichedChannel"
output-channel="firstStep" ref="validateOne" />
<gateway id="validateOne" default-request-channel="ch1"
error-channel="errors1" />
<chain input-channel="ch1">
<service-activator id="mockServiceOneActivator"
ref="mockServiceOne" method="register" />
</chain>
<!-- enrich-header with payload to be available down the line -->
<header-enricher input-channel="firstStep"
output-channel="secondStep">
<header name="initialInfo" expression="getPayload()" />
</header-enricher>
<!-- second-step -->
<service-activator input-channel="secondStep"
output-channel="enrichWithTwoChannel" ref="validateTwo" />
<gateway id="validateTwo" default-request-channel="ch2"
error-channel="errors2" />
<chain input-channel="ch2">
<service-activator id="mockServiceTwoActivator"
ref="mockServiceTwo" method="callExternal" />
</chain>
<!-- enrich-header with payload to be available down the line -->
<header-enricher input-channel="enrichWithTwoChannel"
output-channel="eligibilityCheck">
<header name="serviceTwoInfo" expression="getPayload()" />
</header-enricher>
<!-- final-step -->
<service-activator input-channel="eligibilityCheck"
output-channel="finalChannel" id="mockServiceFinalActivator"
ref="mockServiceFinal" method="submit" />
<!-- error handling -->
<service-activator input-channel="errors1"
output-channel="finalChannel" ref="traceErrorHandler"
method="handleFailedTrace" />
<service-activator input-channel="errors2"
output-channel="finalChannel" ref="traceErrorHandler2"
method="handleFailedTrace" />
<channel id="finalChannel">
<queue />
</channel>
<service-activator input-channel="errorChannel"
ref="globalExceptionHandler" method="handleError" />
My handler code looks like this ..
#Component("traceErrorHandler")public class TraceErrorHandler {
public Message<FinalMessage> handleFailedTrace(Message<?> errorMessage) {
MessagingException payload = (MessagingException) errorMessage.getPayload();
InitialMessage im = (InitialMessage) payload.getFailedMessage().getHeaders().get("initialMessage");
ServiceOneException error = (ServiceOneException) payload.getCause();
FinalMessage v = new FinalMessage(im.getFrom(), im.getTo(), error.getErrorTemplate());
Message<FinalMessage> finalMessage = MessageBuilder.withPayload(v).copyHeaders(payload.getFailedMessage().getHeaders()).build();
return finalMessage;
}}
I am not sure if this is the correct approach in regards to error-handling. The initial chains are simple activators but further down the line there will be more logic.
Should we I always use chains to handle separate error channels
Even if its a single activator service called.
Only when there are Multiple service-activator/transforms that encapsulate a single point of error?
EDIT 1
I added a request-handler-advice-chain reference a bean.
<int:service-activator
id="serviceOneActivator" input-channel="enrichedChannel"
ref="serviceOne" method="register" output-channel="firstStep">
<int:request-handler-advice-chain>
<ref bean="myclass" />
</int:request-handler-advice-chain></int:service-activator>
The reference bean is at first a default definition in xml
<bean id="myclass"
class="org.springframework.integration.handler.advice.ExpressionEvaluatingRequestHandlerAdvice">
<property name="failureChannel" ref="finalChannel" />
<property name="onFailureExpression" value="#payload"/>
<property name="returnFailureExpressionResult" value="true" />
</bean>
if I add the <property name="onFailureExpression" value="#payload"/> property it fails with:
Cannot convert value of type 'java.lang.String' to required type 'org.springframework.expression.Expression' for property 'onFailureExpression'
What i would like to do is convert the exception message to a final message object? but all expression i have added seem to fail on load.
A chain with one component makes no sense.
A chain is syntactic sugar only, each component still stands alone at runtime.
If you want to handle errors for individual endpoints, consider using ExpressionEvaluatingRequestHandlerAdvices instead.
See https://docs.spring.io/spring-integration/docs/5.3.2.RELEASE/reference/html/messaging-endpoints.html#message-handler-advice-chain

Subflow invocation not working in Spring webflow

I am creating a ShoppingCart Application in Spring mvc.Here I have two flows for now.
Registration Flow(flow id=registrationFlow)
MailSender Flow(flow id=mailFlow)
When user registration data gets entered in DB successfully a mailSender flow(Subflow) will get triggered.
My Parent Flow is RegistrationFlow.
Please find below Subflow invocation code:
<!-- other navigation rules of parent flow -->
<subflow-state id="mailSenderFlow" subflow="mailFlow">
<input name="userEmail" value="flowScope.regBean.userDTO.userMail"/>
<transition on="finishMailFlow" to="checkMailFlowResult" />
</subflow-state>
<decision-state id="checkMailFlowResult">
<if test="mailSender.mailConfirmation(currentEvent.attributes.mailFlowOutcome)"
then="regSuccess" else="regConfirm" />
</decision-state>
<end-state id="regSuccess" view="/WEB-INF/view/regSuccess.jsp" />
Based on subflow outcome I have created a decision state which will take the control to regSuccess page or back to regConfirm page.
Please find below Subflow definition file:
<flow xmlns="http://www.springframework.org/schema/webflow"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/webflow
http://www.springframework.org/schema/webflow/spring-webflow-2.4.xsd">
<input name="userEmail" required="true" type="java.lang.String"/>
<action-state id="mailSenderAction">
<evaluate expression="mailSender.sendEmail(userEmail)" />
<transition on="success" to="finishMailFlow" />
</action-state>
<end-state id="finishMailFlow">
<output name="mailFlowOutcome" value="mail sending done"/>
</end-state>
</flow>
Now during subflow invocation time I am getting following exception :
org.springframework.webflow.execution.FlowExecutionException: Exception thrown in state 'mailSenderFlow' of flow 'registrationFlow'
at org.springframework.webflow.engine.impl.FlowExecutionImpl.wrap(FlowExecutionImpl.java:573)
at org.springframework.webflow.engine.impl.FlowExecutionImpl.resume(FlowExecutionImpl.java:263)
at org.springframework.webflow.executor.FlowExecutorImpl.resumeExecution(FlowExecutorImpl.java:169)
at org.springframework.webflow.mvc.servlet.FlowHandlerAdapter.handle(FlowHandlerAdapter.java:253)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:967)
Truncated. see log file for complete stacktrace
Caused By: org.springframework.expression.spel.SpelParseException: EL1041E: After parsing a valid expression, there is still more data in the expression: 'sending'
at org.springframework.expression.spel.standard.InternalSpelExpressionParser.doParseExpression(InternalSpelExpressionParser.java:130)
at org.springframework.expression.spel.standard.SpelExpressionParser.doParseExpression(SpelExpressionParser.java:60)
at org.springframework.expression.spel.standard.SpelExpressionParser.doParseExpression(SpelExpressionParser.java:32)
at org.springframework.expression.common.TemplateAwareExpressionParser.parseExpression(TemplateAwareExpressionParser.java:73)
at org.springframework.binding.expression.spel.SpringELExpressionParser.parseSpelExpression(SpringELExpressionParser.java:96)
Truncated. see log file for complete stacktrace>
Can anyone figure out the issue???
try with single quote inside your value:
<output name="mailFlowOutcome" value="'mail sending done'"/>

Action class method not executed after validation in spring Webflow

I have added a validation in my flow and it works fine, the only problem is I am not able to execute my action class method after the validation. If I remove the validation it works fine.
Here is what my flow.xml looks like
<view-state id="myView" view="newView" model="viewModel">
<transition on="doChangeView" to="doChangeView" />
<transition on="done" to="home" validate="false" />
</view-state>
<action-state id="doChangeView">
<evaluate expression="viewAction.doChangeView" />
<transition on="done" to="home" />
</action-state>
I am guessing that I am not setting the flow correctly. Any help will be appreciated.
In my case, after looking through logs I found that there was some error in the validation method. After resolving it I was able to execute the method in my action class.

Spring Web Flow - Handle concurrent access

I have a very basic flow which looks like this :
<?xml version="1.0" encoding="UTF-8"?>
<flow xmlns="http://www.springframework.org/schema/webflow" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:faces="http://www.springframework.org/schema/faces"
xsi:schemaLocation="
http://www.springframework.org/schema/webflow
http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd">
<view-state id="gestionParametres" view="gestionParametres.xhtml">
<on-entry>
<evaluate expression="gestionParametresAction.initialiser()" />
</on-entry>
<transition on="annulerParametres">
<evaluate expression="gestionParametresAction.annulerParametres()"/>
</transition>
<transition on="enregistrerParametres">
<evaluate expression="gestionParametresAction.enregistrerParametres()"/>
</transition>
<!-- More transitions -->
</view-state>
<end-state id="back"/>
</flow>
Now when I render my page from two different navigators, a change in one page will provoke the same change in the other page. So I want to implement a mechanism that allow my flow to handle concurrent access. How can I achieve that ? I read the spring web flow documentation but I saw nothing about it. May be I am not looking in the right direction...
Thank you.
I solved it using the annotation #Scope("session") on my bean. Obviously the default scope with Spring is #Scope("singleton"), so if I understand correctly, the same instance of the bean was used for every flow using the bean. Here is an other thread that helped me.

Spring Webflow IllegalStateException: Neither BindingResult nor plain target object for bean name available as request attribute

My ultimate goal is to have a model with annotations and do validations on form inputs. I have tried that with writing some validators but it didn't work (The form would not even show fields where I could enter sp there was nothing to test the validation on), so I was trying it from the start in little steps. As a first step I just want to make the model binding work before I start any sort of validation.
As a simple try, I used the mailItemModel form http://www.javabeat.net/introduction-to-spring-web-flow-swf/
I have added the following line to the servlet-config.xml, which is linked in the web.xml:
<mvc:annotation-driven />
<context:annotation-config />
<context:component-scan base-package="com.mypackage.kumo.**" />
I had that from another tutorial and I kind of thought this would make is scan the whole package and I would not need to specify all the models in variables explicitly. I get the titular error.
I have also tried adding "#Component" to the model class. But that didn't change anything.
Into the flow XML I then tried adding
<var name="MailItemModel" />
as from the above linked tutorial. I got the error, that element var" requires a "class" attribute. So I make it this instead:
<var name="MailItemModel" class="com.mypackage.kumo.model.MailItemModel" />
but that just trows the very same error as before.
My flow looks like this now:
<?xml version="1.0" encoding="UTF-8"?>
<flow xmlns="http://www.springframework.org/schema/webflow"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:ns0="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/webflow
http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd"
start-state="Start">
<var name="MailItemModel" class="com.mypackage.kumo.model.MailItemModel" />
<view-state id="Start" view="/Search" model="MailItemModel">
<transition on="entered" to="temp" />
<transition on="cancel" to="SubEnde" />
</view-state>
<view-state id="temp" view="/End" />
<view-state id="SubEnde" view="/Start" />
</flow>
Search.jsp (Form excerpt)
<form:form method = "post" modelAttribute = "mailItemModel">
Mail Username: <form:input path="username" />
<br />
<input type="submit" name="_eventId_entered" value="Entered" />
<input type="submit" name="_eventId_cancel" value="Cancel" />
</form:form>
Start.jsp and End.jsp are just static jsps with some lorem ipsum content.
The Stacktrace:
root cause
java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'mailItemModel' available as request attribute
org.springframework.web.servlet.support.BindStatus.<init>(BindStatus.java:144)
org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.getBindStatus(AbstractDataBoundFormElementTag.java:168)
org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.getPropertyPath(AbstractDataBoundFormElementTag.java:188)
org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.getName(AbstractDataBoundFormElementTag.java:154)
org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.autogenerateId(AbstractDataBoundFormElementTag.java:141)
org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.resolveId(AbstractDataBoundFormElementTag.java:132)
org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.writeDefaultAttributes(AbstractDataBoundFormElementTag.java:116)
org.springframework.web.servlet.tags.form.AbstractHtmlElementTag.writeDefaultAttributes(AbstractHtmlElementTag.java:422)
org.springframework.web.servlet.tags.form.InputTag.writeTagContent(InputTag.java:142)
org.springframework.web.servlet.tags.form.AbstractFormTag.doStartTagInternal(AbstractFormTag.java:84)
org.springframework.web.servlet.tags.RequestContextAwareTag.doStartTag(RequestContextAwareTag.java:80)
org.apache.jsp.WEB_002dINF.jsp.Search_jsp._jspx_meth_form_005finput_005f0(Search_jsp.java:559)
org.apache.jsp.WEB_002dINF.jsp.Search_jsp._jspx_meth_form_005fform_005f0(Search_jsp.java:522)
org.apache.jsp.WEB_002dINF.jsp.Search_jsp._jspx_meth_dsx_005fcontent_002dbox_005f0(Search_jsp.java:478)
org.apache.jsp.WEB_002dINF.jsp.Search_jsp._jspx_meth_dsx_005fcontent_005f0(Search_jsp.java:439)
org.apache.jsp.WEB_002dINF.jsp.Search_jsp._jspx_meth_dsx_005fhtml_002dbody_005f0(Search_jsp.java:375)
org.apache.jsp.WEB_002dINF.jsp.Search_jsp._jspx_meth_dsx_005fhtml_005f0(Search_jsp.java:293)
org.apache.jsp.WEB_002dINF.jsp.Search_jsp._jspService(Search_jsp.java:126)
org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:70)
javax.servlet.http.HttpServlet.service(HttpServlet.java:728)
org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:432)
org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:390)
org.apache.jasper.servlet.JspServlet.service(JspServlet.java:334)
javax.servlet.http.HttpServlet.service(HttpServlet.java:728)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
org.springframework.web.servlet.view.InternalResourceView.renderMergedOutputModel(InternalResourceView.java:209)
org.springframework.web.servlet.view.AbstractView.render(AbstractView.java:267)
org.springframework.webflow.mvc.servlet.ServletMvcView.doRender(ServletMvcView.java:55)
org.springframework.webflow.mvc.view.AbstractMvcView.render(AbstractMvcView.java:187)
org.springframework.webflow.engine.ViewState.render(ViewState.java:296)
org.springframework.webflow.engine.ViewState.refresh(ViewState.java:243)
org.springframework.webflow.engine.ViewState.resume(ViewState.java:221)
org.springframework.webflow.engine.Flow.resume(Flow.java:545)
org.springframework.webflow.engine.impl.FlowExecutionImpl.resume(FlowExecutionImpl.java:258)
org.springframework.webflow.executor.FlowExecutorImpl.resumeExecution(FlowExecutorImpl.java:169)
org.springframework.webflow.mvc.servlet.FlowHandlerAdapter.handle(FlowHandlerAdapter.java:183)
org.springframework.webflow.mvc.servlet.FlowController.handleRequest(FlowController.java:174)
org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter.handle(SimpleControllerHandlerAdapter.java:50)
org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:938)
org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:870)
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:961)
org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:852)
javax.servlet.http.HttpServlet.service(HttpServlet.java:621)
org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:837)
javax.servlet.http.HttpServlet.service(HttpServlet.java:728)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
What am I doing wrong? I have looked into other posts of the same error, but they all use #requestmapping to an URL in a controller, but since I am using Webflow, I don't (and can't it would seem) use controllers, so it didn't really help.
There are 2 things flawed in your configuration
Your name of the model is wrong
Your base package is a ant-style expression
In your configuration you named your model object MailItemModel (notice the M)
<var name="MailItemModel" class="com.mypackage.kumo.model.MailItemModel" />
In your view it is named mailItemModel (notice the m).
<form:form method = "post" modelAttribute = "mailItemModel">
Either change it in your flow configuration to mailItemModel or in your view use MailItemModel the names have to match, including the casing.
Regarding your configuration.
<context:component-scan base-package="com.mypackage.kumo.**" />
The base-package property is just that the name(s) of the base packages to scan. It doesn't take an ant-style expression. Remove the ending .**.
<context:component-scan base-package="com.mypackage.kumo" />
A final note the use of <context:component-scan … /> already implies <context:annotation-config /> so you can remove that line of configuration.

Categories

Resources