I'm trying to set up Spring Web Flow using only Java annotations in a Spring environment that also uses only Java annotations. However when I attempt to access my flow I get the following exception
SEVERE: Servlet.service() for servlet [dispatcher] in context with path [/forms] threw exception [Request processing failed; nested exception is org.springframework.webflow.execution.FlowExecutionException: Exception thrown in state 'referral' of flow 'test-flow'] with root cause
java.lang.ClassCastException: org.springframework.context.support.GenericApplicationContext cannot be cast to org.springframework.web.context.WebApplicationContext
at org.springframework.web.servlet.support.RequestContext.initContext(RequestContext.java:235)
at org.springframework.web.servlet.support.RequestContext.<init>(RequestContext.java:202)
at org.springframework.web.servlet.view.AbstractView.createRequestContext(AbstractView.java:316)
at org.springframework.web.servlet.view.AbstractView.createMergedOutputModel(AbstractView.java:296)
at org.springframework.web.servlet.view.AbstractView.render(AbstractView.java:265)
at org.springframework.webflow.mvc.servlet.ServletMvcView.doRender(ServletMvcView.java:55)
at org.springframework.webflow.mvc.view.AbstractMvcView.render(AbstractMvcView.java:196)
at org.springframework.webflow.engine.ViewState.render(ViewState.java:293)
at org.springframework.webflow.engine.ViewState.refresh(ViewState.java:242)
at org.springframework.webflow.engine.ViewState.resume(ViewState.java:220)
at org.springframework.webflow.engine.Flow.resume(Flow.java:537)
at org.springframework.webflow.engine.impl.FlowExecutionImpl.resume(FlowExecutionImpl.java:259)
at org.springframework.webflow.executor.FlowExecutorImpl.resumeExecution(FlowExecutorImpl.java:169)
at org.springframework.webflow.mvc.servlet.FlowHandlerAdapter.handle(FlowHandlerAdapter.java:228)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:938)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:870)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:961)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:852)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:624)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:837)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:731)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:220)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:505)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:170)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:103)
at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:957)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:423)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1079)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:620)
at org.apache.tomcat.util.net.AprEndpoint$SocketProcessor.doRun(AprEndpoint.java:2476)
at org.apache.tomcat.util.net.AprEndpoint$SocketProcessor.run(AprEndpoint.java:2465)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:745)
I've traced the source of the error to the instantiation of a RequestContext
object, which is passed to views in order for them to access the ApplicationContext
(among other things). Since Spring is able to invoke my flows I'm lead to believe that this exception is being thrown due to a bad configuration somewhere in my Web Flow files. I've reviewed them many times and can't find anything that seems glaringly obvious. You can find my Web Flow configuration files below:
DispatcherConfig
@Configuration
@ComponentScan(basePackages = "----")
public class DispatcherConfig {
@Autowired
private FlowDefinitionRegistry flowDefinitionRegistry;
@Autowired
private FlowExecutor flowExecutor;
@Autowired
private AnnotationConfigWebApplicationContext appContext;
@Bean
public FlowHandlerAdapter flowHandlerAdapter()
{
FlowHandlerAdapter flowHandlerAdapter = new FlowHandlerAdapter();
flowHandlerAdapter.setFlowExecutor(flowExecutor);
return flowHandlerAdapter;
}
@Bean
public FlowHandlerMapping flowHandlerMapping()
{
FlowHandlerMapping mapping = new FlowHandlerMapping();
mapping.setFlowRegistry(flowDefinitionRegistry);
mapping.setApplicationContext(appContext);
mapping.setOrder(-1);
return mapping;
}
}
WebFlowConfig
@Configuration
public class WebFlowConfig extends AbstractFlowConfiguration {
@Autowired
private List<ViewResolver> viewResolvers;
@Autowired
private ConversionService conversionService;
@Autowired
private AnnotationConfigWebApplicationContext appContext;
@Bean
public FlowDefinitionRegistry flowRegistry()
{
return getFlowDefinitionRegistryBuilder()
.setBasePath("/WEB-INF/flows")
.addFlowLocationPattern("/**/*-flow.xml")
.setFlowBuilderServices(getFlowBuilderServices())
.build();
}
@Bean
public FlowExecutor flowExecutor()
{
return getFlowExecutorBuilder(flowRegistry()).build();
}
public ExpressionParser getExpressionParser()
{
SpelExpressionParser spelExpressionParser = new SpelExpressionParser();
return new WebFlowSpringELExpressionParser(spelExpressionParser);
}
public FlowBuilderServices getFlowBuilderServices()
{
MvcViewFactoryCreator creator = new MvcViewFactoryCreator();
creator.setViewResolvers(viewResolvers);
creator.setApplicationContext(appContext);
creator.setUseSpringBeanBinding(true);
FlowBuilderServices services = new FlowBuilderServices();
services.setViewFactoryCreator(creator);
services.setConversionService(conversionService);
services.setExpressionParser(getExpressionParser());
return services;
}
}
WebMvcConfig
@EnableWebMvc
@Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter {
@Bean
public UrlBasedViewResolver velocityViewResolver()
{
UrlBasedViewResolver resolver = new VelocityViewResolver();
resolver.setSuffix(".vm");
resolver.setViewClass(VelocityView.class);
return resolver;
}
@Bean
public VelocityConfigurer velocityConfig()
{
VelocityConfigurer velocityConfigurer = new VelocityConfigurer();
velocityConfigurer.setResourceLoaderPath("/templates/views/");
return velocityConfigurer;
}
@Bean
public List<ViewResolver> viewResolvers()
{
List<ViewResolver> resolvers = new ArrayList<>();
resolvers.add(velocityViewResolver());
return resolvers;
}
}
The view technology I'm using is Velocity. Spring Webflow version 2.4.1.RELEASE, Spring version 4.1.2.RELEASE, Java 1.8.0_45, Tomcat 7.0.62.
Any ideas what may be causing this exception?
UPDATE: Fixed
Thanks to Roman for providing the answer. I made my FlowBuilderServices
and MvcViewFactoryCreator
@Bean
and this got rid of the ClassCastException
. However, there were still some problems with my configuration. My @Autowired
List<ViewResolver> viewResolvers
was not being instantiated until after the MvcViewFactoryCreator
was. Thus I had to make a method that creates the ViewResolver
list for the MvcViewFactoryCreator
. I'm looking into ways to get the order of creation correct, but for now this works. Below are the changes I had to make to get the Web Flow functioning properly
WebFlowConfig
@Configuration
public class WebFlowConfig extends AbstractFlowConfiguration {
@Autowired
===
-private List<ViewResolver> viewResolvers;
====
+private UrlBasedViewResolver viewResolver
===
...
@Autowired
===
-private AnnotationConfigWebApplicationContext appContext;
===
+private MvcViewFactoryCreator mvcViewFactoryCreator;
===
...
+public List<ViewResolver> getViewResolvers()
+{
+ List<ViewResolver> resolvers = new ArrayList<>();
+ resolvers.add(viewResolver);
+
+ return resolvers;
+}
...
+@Bean
+public MvcViewFactoryCreator mvcViewFactoryCreator()
+{
+ MvcViewFactoryCreator creator = new MvcViewFactoryCreator();
+ creator.setViewResolvers(getViewResolvers());
+ creator.setUseSpringBeanBinding(true);
+ return creator;
+}
@Bean
public FlowBuilderServices flowBuilderServices()
{
===
-MvcViewFactoryCreator creator = new MvcViewFactoryCreator();
-creator.setViewResolvers(viewResolvers);
-creator.setApplicationContext(appContext);
-creator.setUseSpringBeanBinding(true);
===
FlowBuilderServices services = new FlowBuilderServices();
===
-services.setViewFactoryCreator(creator);
===
+services.setViewFactoryCreator(mvcViewFactoryCreator);
===
services.setConversionService(conversionService);
services.setExpressionParser(getExpressionParser());
return services;
}
}
FlowBuilderServices
is meant to be a Spring-managed bean, but in your config it is just anew
instance. It likes to beApplicationContextAware
andInitializingBean
, but that is gonna work only if managed by Spring.The solution is simple: put
@Bean
ongetFlowBuilderServices()
method. And I think you should also makeMvcViewFactoryCreator
a separate@Bean
and not use itssetApplicationContext()
manually.