Spring WebFlow (seemingly) randomly stops working in Spring Boot app -
i have flow seemed working fine until yesterday, when started getting following exception in html page maps first state in flow:
org.springframework.expression.spel.spelevaluationexception: el1007e:(pos 0): property or field 'flowscope' cannot found on null
the offending line of code was:
<h3 th:text="${flowrequestcontext.flowscope}"/>
further investigation showed none of flow variables available anymore. furthermore if put print statements service flow makes various calls to, can see none of these methods being called anymore - it's flow isn't running @ all.
this was working fine previously. reverted of local changes stable version of code, , same issue happening there well. thing seemed temporarily around problem restart computer - problem disappeared short while came back.
to honest i'm out of ideas have started causing such intermittent problem. thinking along lines of stale java process running in background interfering future runs of application, have checked , killed off remaining process in between deploys no avail.
i have included hope relevant file below. resolving issue appreciated.
checkout.xml
<?xml version="1.0" encoding="utf-8"?> <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.0.xsd"> <on-start> <set name="flowscope.paymentmethods" value="checkoutwidgetservice.getpaymentmethods()"/> <set name="flowscope.deliveryaddress" value="checkoutwidgetservice.getdeliveryaddress()"/> <set name="flowscope.sessionid" value="externalcontext.nativerequest.session.id"/> </on-start> <view-state id="payment-methods" view="payment-methods"> <transition on="selectpaymentmethod" to="new-details"> <evaluate expression="checkoutwidgetservice.getcarddetails(requestparameters.type)" result="flowscope.carddetails"/> </transition> </view-state> <view-state id="new-details" view="new-details"> <transition on="submitdetails" to="summary"> <evaluate expression="checkoutwidgetservice.buildcarddetails(requestparameters)" result="flowscope.carddetails"/> </transition> </view-state> <view-state id="summary" view="summary"> <transition on="completecheckout" to="redirect"> <evaluate expression="checkoutwidgetservice.completecheckout(externalcontext.nativerequest.session, flowrequestcontext, flowscope.carddetails)"/> </transition> <transition on="cancelcheckout" to="redirect"> <evaluate expression="checkoutwidgetservice.cancelcheckout(externalcontext.nativerequest.session, flowrequestcontext)"/> </transition> </view-state> <end-state id="redirect" view="externalredirect:contextrelative:/payments/checkout-widgets/end"/> </flow>
webflowconfig.java
@configuration @autoconfigureafter(mvcconfig.class) public class webflowconfig extends abstractflowconfiguration { @autowired private springtemplateengine templateengine; @bean public flowexecutor flowexecutor() { return getflowexecutorbuilder(flowregistry()) .addflowexecutionlistener(new securityflowexecutionlistener()) .build(); } @bean public flowdefinitionregistry flowregistry() { return getflowdefinitionregistrybuilder(flowbuilderservices()) .addflowlocation("classpath:/templates/checkout.xml", "payments/checkout-widget/start") .build(); } @bean public flowbuilderservices flowbuilderservices() { return getflowbuilderservicesbuilder() .setviewfactorycreator(mvcviewfactorycreator()) .setdevelopmentmode(true) .build(); } @bean public flowcontroller flowcontroller() { flowcontroller flowcontroller = new flowcontroller(); flowcontroller.setflowexecutor(flowexecutor()); return flowcontroller; } @bean public flowhandlermapping flowhandlermapping() { flowhandlermapping flowhandlermapping = new flowhandlermapping(); flowhandlermapping.setflowregistry(flowregistry()); flowhandlermapping.setorder(-1); return flowhandlermapping; } @bean public flowhandleradapter flowhandleradapter() { flowhandleradapter flowhandleradapter = new flowhandleradapter(); flowhandleradapter.setflowexecutor(flowexecutor()); flowhandleradapter.setsaveoutputtoflashscopeonredirect(true); return flowhandleradapter; } @bean public ajaxthymeleafviewresolver thymeleafviewresolver() { ajaxthymeleafviewresolver viewresolver = new ajaxthymeleafviewresolver(); viewresolver.setviewclass(flowajaxthymeleafview.class); viewresolver.settemplateengine(templateengine); return viewresolver; } @bean public mvcviewfactorycreator mvcviewfactorycreator() { list<viewresolver> viewresolvers = new arraylist<>(); viewresolvers.add(thymeleafviewresolver()); mvcviewfactorycreator mvcviewfactorycreator = new mvcviewfactorycreator(); mvcviewfactorycreator.setviewresolvers(viewresolvers); mvcviewfactorycreator.setusespringbeanbinding(true); return mvcviewfactorycreator; } }
checkoutwidgetsessionmvccontroller.java
@controller @requestmapping("/payments/checkout-widgets") public class checkoutwidgetsessionmvccontroller { @inject private checkoutwidgetservice service; @requestmapping(value = {"/start"}, method = requestmethod.get) public modelandview paymentmethods() { return new modelandview("payment-methods", null); } @requestmapping(value = "/end", method = requestmethod.get) public string invalidatesession(httpsession session) { service.invalidatesession(session); return "dummy-redirect-post"; } }
checkoutwidgetservice.java
public interface checkoutwidgetservice { list<paymentmethod> getpaymentmethods(); carddetails getcarddetails(string name); carddetails buildcarddetails(localparametermap params); string getdeliveryaddress(); void completecheckout(httpsession session, requestcontext context, carddetails carddetails); void cancelcheckout(httpsession session, requestcontext context); void invalidatesession(httpsession session); }
checkoutwidgetserviceimpl.java
@service("checkoutwidgetservice") public class checkoutwidgetserviceimpl implements checkoutwidgetservice { @inject private checkoutwidgetsessionservice sessionservice; private final list<paymentmethod> paymentmethods = new arraylist<>(); private final string deliveryaddress; public checkoutwidgetserviceimpl() { paymentmethods.add(new paymentmethod("paypal", "/images/paypal-logo.png")); paymentmethods.add(new paymentmethod("mastercard", "/images/mc-logo.png")); paymentmethods.add(new paymentmethod("visa", "/images/visa-logo.png")); paymentmethods.add(new paymentmethod("amex", "/images/amex-logo.png")); paymentmethods.add(new paymentmethod("google checkout", "/images/google-logo.png")); deliveryaddress = "xxxxx"; } @override public list<paymentmethod> getpaymentmethods() { system.out.println("returning paymentmethods: " + paymentmethods); return paymentmethods; } @override public carddetails getcarddetails(string name) { carddetails carddetails = new carddetails(); carddetails.setcardtype(name); return carddetails; } @override public carddetails buildcarddetails(localparametermap params) { carddetails carddetails = new carddetails(); carddetails.setcardnumber(params.get("cardnumber")); carddetails.setexpirymonth(params.get("expirymonth")); carddetails.setexpiryyear(params.get("expiryyear")); carddetails.setnameoncard(params.get("nameoncard")); carddetails.setcvv2(params.get("cvv2")); return carddetails; } @override public string getdeliveryaddress() { return deliveryaddress; } @override public void invalidatesession(httpsession session) { session.invalidate(); } private redirecturls getredirecturls(string sessionid) { checkoutwidgetsession widgetsession = sessionservice.getcheckoutwidgetsession(sessionid).get(); return widgetsession.getredirecturls(); } @override public void completecheckout(httpsession session, requestcontext context, carddetails carddetails) { redirecturls redirects = getredirecturls(session.getid()); context.getflowscope().remove("paymentmethods"); uribuilder uribuilder = uribuilder.fromuri(uri.create(redirects.getsuccessurl())); string forwardurl = uribuilder.queryparam("transactionid", "12345").tostring(); context.getflowscope().put("forwardurl", forwardurl); context.getflowscope().put("target", "_top"); } @override public void cancelcheckout(httpsession session, requestcontext context) { redirecturls redirects = getredirecturls(session.getid()); context.getflowscope().remove("paymentmethods"); string forwardurl = redirects.getcancelurl(); context.getflowscope().put("forwardurl", forwardurl); context.getflowscope().put("target", "_top"); } }
application.java
@enableautoconfiguration @componentscan(basepackages = {"com.pay.widgets.checkout"}) @import(jerseyautoconfiguration.class) public class application extends springbootservletinitializer { @override protected springapplicationbuilder configure(springapplicationbuilder application) { return application.sources(application.class); } public static void main(string[] args) { springapplication.run(application.class, args); } }
okay stupid mistake on part, problem turned out down typo in @requestparameter annotation on controller:
payments/checkout-widgets
which didn't line in webflowconfig defined flowregistry:
payments/checkout-widget
i can assume resource cached tomcat why took long issue manifest , threw me off scent in terms of suspecting own changes responsible.
Comments
Post a Comment