Spring WebFlow (seemingly) randomly stops working in Spring Boot app - spring-boot

I have a flow which seemed to be working fine until yesterday, when suddenly I started getting the following exception in my HTML page that maps to the first state in my flow:
org.springframework.expression.spel.SpelEvaluationException: EL1007E:(pos 0): Property or field 'flowScope' cannot be found on null
The offending line of code was:
<h3 th:text="${flowRequestContext.flowScope}"/>
Further investigation showed that none of the flow variables are available anymore. Furthermore if I put print statements into the Service which the flow makes various calls to, I can see that none of these methods are being called anymore - it's like the flow just isn't running at all.
This was working fine previously. I even reverted all of my local changes to a previously stable version of the code, and the same issue was happening there as well. The only thing that seemed to temporarily get around the problem was to restart my computer - the problem disappeared for a short while but then came back.
To be honest I'm completely out of ideas as to what could have started causing such an intermittent problem. I was thinking along the lines of a stale Java process running in the background interfering with future runs of the application, but have checked for and killed off any remaining process in between deploys to no avail.
I have included what I hope are the relevant file below. Any help resolving this issue would be very much 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 this was a really stupid mistake on my part, the problem turned out to be down to a typo in the #RequestParameter annotation on the Controller:
payments/checkout-widgets
Which didn't line up with what was in the WebflowConfig defined flowRegistry:
payments/checkout-widget
I can only assume the resource was cached by Tomcat which is why it took so long for the issue to manifest and threw me off the scent in terms of suspecting my own changes to be responsible.

Related

PayloadRootSmartSoapEndpointInterceptor Intercepts multiple EndPoints

I'm trying to add a Custom Interceptors to the interceptors List in my EndPoint Config, but i have a problem where PayloadRootSmartSoapEndpointInterceptor intercepts 2 of my Endpoints instead of one, I have Defined 2 SOAP EndPoints using spring-ws.
#EnableWs
#Configuration
#Order(1)
public class Config extends WsConfigurerAdapter {
private String namespaceBti = "http://tarim.bull.ro/BullTarimWS/BTIService";
private String namespaceBtiLst = "http://tarim.bull.ro/BullTarimWS/BTILSTService";
#Bean
public ServletRegistrationBean messageDispatcherServlet(ApplicationContext applicationContext) {
MessageDispatcherServlet servlet = new MessageDispatcherServlet();
servlet.setApplicationContext(applicationContext);
servlet.setTransformWsdlLocations(true);
return new ServletRegistrationBean(servlet, "/public/btiWS/*");
}
//Service 1
#Bean(name = "BTIService")
public DefaultWsdl11Definition defaultWsdl11DefinitionBti(#Qualifier("BTISchema") XsdSchema certificateSchema) {
DefaultWsdl11Definition wsdl11Definition = new DefaultWsdl11Definition();
wsdl11Definition.setPortTypeName("BtiPort");
wsdl11Definition.setLocationUri("/public/btiWS"); //<context-path>
wsdl11Definition.setTargetNamespace(namespaceBti);
wsdl11Definition.setRequestSuffix("Input");
wsdl11Definition.setResponseSuffix("Output");
wsdl11Definition.setSchema(certificateSchema);
return wsdl11Definition;
}
#Bean(name="BTISchema")
public XsdSchema certificateSchemaBti() {
return new SimpleXsdSchema(new ClassPathResource("xml-resources/GETBTI.xsd"));
}
// Service 2
#Bean(name = "BTILSTService") //name of the wsdl in the URL
public DefaultWsdl11Definition defaultWsdl11DefinitionBtiLst(#Qualifier("BTILSTSchema") XsdSchema certificateSchema) {
DefaultWsdl11Definition wsdl11Definition = new DefaultWsdl11Definition();
wsdl11Definition.setPortTypeName("BtiLstPort");
wsdl11Definition.setLocationUri("/public/btiWS"); //<context-path>
wsdl11Definition.setTargetNamespace(namespaceBtiLst);
wsdl11Definition.setRequestSuffix("Input");
wsdl11Definition.setResponseSuffix("Output");
wsdl11Definition.setSchema(certificateSchema);
return wsdl11Definition;
}
#Bean(name="BTILSTSchema")
public XsdSchema certificateSchemaBtiLst() {
return new SimpleXsdSchema(new ClassPathResource("xml-resources/GETBTILST.xsd"));
}
#Autowired
private WriteBtiDto writeBtiDto;
Adding a Custom Interceptor to the list>
#Override
public void addInterceptors(List<EndpointInterceptor> interceptors) {
interceptors.add(new PayloadRootSmartSoapEndpointInterceptor(
new BtiEndpointInterceptor(), //let Spring Build and Manage The Bean, not me
BtiEndpoint.getNamespaceUri(),
BtiEndpoint.getLocalPart()
));
}
BTI EndPoint
#Endpoint()
public class BtiEndpoint {
private static final String NAMESPACE_URI="http://tarim.bull.ro/BullTarimWS/BTIService";
private static final String LOCAL_PART = "CXMLTYPE-GETBTIInput";
#PayloadRoot(namespace = NAMESPACE_URI, localPart = LOCAL_PART)
#ResponsePayload
public CXMLTYPEGETBTIOutput getBTI(#RequestPayload CXMLTYPEGETBTIInput request){
CXMLTYPEGETBTIOutput response = new CXMLTYPEGETBTIOutput();
return response;
}
// GETTERS AND SETTER FOR NAMESPACE AND LOCAL PART
BTILST EndPoint
#Endpoint()
public class BtiLstEndpoint {
private static final String NAMESPACE_URI="http://tarim.bull.ro/BullTarimWS/BTILSTService";
private static final String LOCAL_PART = "CXMLTYPE-GETBTILSTInput";
#PayloadRoot(namespace = NAMESPACE_URI, localPart = LOCAL_PART)
#ResponsePayload
public CXMLTYPEGETBTILSTOutput getBTI(#RequestPayload CXMLTYPEGETBTILSTInput request){
CXMLTYPEGETBTILSTOutput response = new CXMLTYPEGETBTILSTOutput();
return response;
}
// GETTERS AND SETTER FOR NAMESPACE AND LOCAL PART
EndpointInterceptor
#Component
public class BtiEndpointInterceptor implements EndpointInterceptor {
private static final Log LOG = LogFactory.getLog(BtiEndpointInterceptor.class);
#Override
public boolean handleRequest(MessageContext messageContext, Object o) throws Exception {
LOG.info("1. Global Request Handling");
return true;
}
#Override
public boolean handleResponse(MessageContext messageContext, Object o) throws Exception {
LOG.info("2. Global Response Handling");
return true;
}
#Override
public boolean handleFault(MessageContext messageContext, Object o) throws Exception {
LOG.info("Global Exception Handling");
return true;
}
#Override
public void afterCompletion(MessageContext messageContext, Object endpoint, Exception ex) {
}

Controller are not working in Spring

I unable to understand why my controller are not redirecting to my html. Anyone can help me please?
WebConfig.java
#Configuration
#EnableWebMvc
#ComponentScan(basePackages = { "com.udemy.controller" })
public class WebConfig extends WebMvcConfigurerAdapter {
#Bean
public InternalResourceViewResolver resolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setViewClass(JstlView.class);
resolver.setPrefix("/WEB-INF/views/");
resolver.setSuffix("");
return resolver;
}
}
HelloWorldController.java
#Controller
#RequestMapping("/example")
public class HelloWorldController {
public static final String EXAMPLE_VIEW = "example.html";
#GetMapping("/")
public String fileUploadForm(Model model) {
return "fileDownloadView";
}
#GetMapping("/helloworld")
public String helloWorld(){
return "helloworld";
}
// #RequestMapping(value="/exampleString", method=RequestMethod.GET)
#GetMapping("/exampleString")
public String exampleString(Model model){
model.addAttribute("name","John");
return EXAMPLE_VIEW;
}
// #RequestMapping(value="/exampleMAV", method=RequestMethod.GET)
#GetMapping("/exampleMAV")
public ModelAndView exampleMAV() {
ModelAndView mav= new ModelAndView(EXAMPLE_VIEW);
mav.addObject("name", "Mike");
return mav;
}
AppInitializer
public class MyWebAppInitializer
extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] {};
}
#Override
protected Class<?>[] getServletConfigClasses() {
return new Class[] { WebConfig.class };
}
#Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
}
My project structure is well done. So my html and jsps, are inside of the root WEB-INF/views. Also, the anotation #ComponentScan, are detecting the controller. So, its not a problem of root. Anyone can tell me, why im am not redirecting to the .html , please..
Error says:
ADVERTENCIA: No mapping found for HTTP request with URI [/spring-mvc-download-example/WEB-INF/views/example.html] in DispatcherServlet with name 'dispatcher'
In your controller class, above the
#RequestMapping("/example")
Insert:
#Controller
Gonna be:
#Controller
#RequestMapping("/example")
you have to annotate class HelloWorldController with #Controller or #RestController, only then it will be picked by #Componentscan annotation.

How to get the session object in general from within a class?

There is a class :
#Configuration
#ComponentScan("com.ambre.pta")
#EnableTransactionManagement
#PropertySources({
#PropertySource("classpath:fr/global.properties"),
#PropertySource("classpath:fr/main.properties"),
#PropertySource("classpath:fr/admin.properties"),
#PropertySource("classpath:fr/referentiel.properties"),
#PropertySource("classpath:fr/departement.properties"),
#PropertySource("classpath:fr/exercice.properties"),
#PropertySource("classpath:fr/defi.properties")
})
public class ApplicationContextConfig {
#Bean
public static PropertySourcesPlaceholderConfigurer properties() {
return new PropertySourcesPlaceholderConfigurer();
}
#Bean(name = "viewResolver")
public InternalResourceViewResolver getViewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setPrefix("/WEB-INF/views/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
#Bean(name = "dataSource")
public DataSource getDataSource() {
BasicDataSource bds = new BasicDataSource();
bds.setDriverClassName("oracle.jdbc.driver.OracleDriver");
bds.setUrl("jdbc:oracle:thin:#localhost:1521:xe");
bds.setUsername("pta");
bds.setPassword("pta");
return bds;
}
#Autowired
#Bean(name = "sessionFactory")
public SessionFactory getSessionFactory(DataSource dataSource) {
LocalSessionFactoryBuilder sessionBuilder = new LocalSessionFactoryBuilder(dataSource);
sessionBuilder.scanPackages("com.ambre.pta.model");
return sessionBuilder.buildSessionFactory();
}
#Autowired
#Bean(name = "transactionManager")
public HibernateTransactionManager getTransactionManager(SessionFactory sessionFactory) {
HibernateTransactionManager transactionManager = new HibernateTransactionManager(sessionFactory);
return transactionManager;
}
#Autowired
#Bean(name = "utilisateurDao")
public UtilisateurDAO getUtilisateurDao(SessionFactory sessionFactory) {
return new UtilisateurDAOImpl(sessionFactory);
}
}
We can get the request object within this class by this way :
HttpServletRequest request = ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest();
So is there a way to get the session ( HttpSession ) object ?
Using Controller classes
You can define HttpSession as an argument to your controller methods:
#RequestMapping("/my-path")
public String doStuff(HttpSession session) {
// do stuff...
}
This is also the recommended way to access the HttpRequest, rather than going through RequestContextHolder, because it's easier to mock out the session for tests.
See the Spring MVC docs for more detail.
Without using controller classes
Another option is to use a #SessionScope bean and mutate it accordingly:
#Component
#SessionScope
public class MySessionScopedBean {
String attribute;
}
public class MyOtherClass {
#Autowired
private MySessionScopedBean myBean;
public void doStuff() {
// myBean accesses a bean instance isolated in the user session
myBean.attribute = "test";
}
}
If you want to access the HttpSession outside a Controller method, there are two possible answers:
Don't do it, it's evil.
If you really really want to do it (and I have done it in the past, so who am I to blame you), then the easiest way is to write a class to hold the session, and an interceptor to set and unset it. But to make things easier, I will be working with HttpServletRequest objects, from which you can extract the session:
public class RequestHolder {
private static final ThreadLocal<HttpServletRequest> REQUEST =
new ThreadLocal<>();
public static HttpServletRequest getCurrentRequest() {
return REQUEST.get();
}
public static HttpSession getCurrentSession() {
HttpServletRequest request = REQUEST.get();
return request == null ? null : request.getSession();
}
public static void setCurrentRequest(HttpServletRequest request) {
REQUEST.set(request);
}
public static void unsetCurrentRequest() {
REQUEST.remove();
}
}
public class RequestHolderInterceptor implements HandlerInterceptor {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object o) {
RequestHolder.setCurrentRequest(request);
return true;
}
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object o, ModelAndView mav) {
}
#Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object o, Exception e){
RequestHolder.unsetCurrentRequest();
}
}

flowExecutionUrl no redirect

I've followed some examples for redirecting in SWF and every do it equal. In my case the link don't redirect, execution reference don't change and url has changed to href's value. If I set it in a commandButton then the result is succes.
I use JSF 2.2 and SWF 2.3 on glassfish 4. Any suggestions> Thanks!
<a href="${flowExecutionUrl}&_eventId=TRANSITION_NAME" >
My link
<a href="${flowExecutionUrl}&_eventId=visit" >
URL result
http://localhost:8080/aio/spring/adminSpace?execution=e4s3&_eventId=visit
Flow definition
<view-state id="showEvent" view="list_events.xhtml">
<secured attributes="ROLE_MEMBER" />
<on-entry>
<evaluate expression="eventProvider.showEvents()" result="viewScope.pagesList"/>
</on-entry>
<transition on="pageCreated" to="newEvent" />
<transition on="visit" to="visitEvent"/>
</view-state>
<view-state id="newEvent" view="newEvent_1.xhtml">
<secured attributes="ROLE_MEMBER" />
<on-render>
<set name="pictureProvider.idRequest" value="'event'" />
</on-render>
<transition on="back" to="showEvent" />
<transition on="save" to="showEvent" />
<on-exit>
<evaluate expression="eventProvider.deleteTempComponents()" />
</on-exit>
</view-state>
<view-state id="visitEvent" view="event.xhtml">
</view-state>
WebFlowConfig class
#Configuration
public class WebFlowConfig extends AbstractFacesFlowConfiguration {
#Bean
public FlowExecutor flowExecutor() {
return getFlowExecutorBuilder(flowRegistry())
.addFlowExecutionListener(new FlowFacesContextLifecycleListener())
.addFlowExecutionListener(new SecurityFlowExecutionListener())
.build();
}
#Bean
public FlowDefinitionRegistry flowRegistry() {
return getFlowDefinitionRegistryBuilder(flowBuilderServices())
.setBasePath("/WEB-INF/flows")
.addFlowLocationPattern("/**/*-flow.xml")
.build();
}
#Bean
public FlowBuilderServices flowBuilderServices() {
return getFlowBuilderServicesBuilder()
//.setViewFactoryCreator(new JsfViewFactoryCreator())
.setDevelopmentMode(true).build();
}
#Bean
public CustomScopeConfigurer customScopeConfigurer() {
Map<String, Object> scopes = new HashMap<String, Object>();
scopes.put("view", new ViewScope());
CustomScopeConfigurer configurer = new CustomScopeConfigurer();
configurer.setScopes(scopes);
return configurer;
}
}
WebMvcConfig class
#EnableWebMvc
#Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter{
#Autowired
private WebFlowConfig webFlowConfig;
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**").addResourceLocations("classpath:/resources/");
}
#Bean
public FlowHandlerMapping flowHandlerMapping() {
FlowHandlerMapping mapping = new FlowHandlerMapping();
mapping.setOrder(1);
mapping.setFlowRegistry(this.webFlowConfig.flowRegistry());
/* If no flow matches, map the path to a view, e.g. "/intro" maps to a view named "intro" */
mapping.setDefaultHandler(new UrlFilenameViewController());
return mapping;
}
#Bean
public FlowHandlerAdapter flowHandlerAdapter() {
JsfFlowHandlerAdapter adapter = new JsfFlowHandlerAdapter();
adapter.setFlowExecutor(this.webFlowConfig.flowExecutor());
return adapter;
}
#Bean
public UrlBasedViewResolver faceletsViewResolver() {
UrlBasedViewResolver resolver = new UrlBasedViewResolver();
resolver.setViewClass(JsfView.class);
resolver.setPrefix("/WEB-INF/");
resolver.setSuffix(".xhtml");
return resolver;
}
#Bean
public SimpleControllerHandlerAdapter simpleControllerHandlerAdapter() {
return new SimpleControllerHandlerAdapter();
}
#Bean
public SessionLocaleResolver localeResolver(){
SessionLocaleResolver localeResolver=new SessionLocaleResolver();
localeResolver.setDefaultLocale(new Locale("es"));
return localeResolver;
}
#Bean
public ReloadableResourceBundleMessageSource messageSource(){
ReloadableResourceBundleMessageSource msg=new ReloadableResourceBundleMessageSource();
msg.setBasename("classpath*:error");
return msg;
}
#Bean
public StandardServletMultipartResolver multipartResolver(){
return new StandardServletMultipartResolver();
}
/*
#Bean
public LocaleChangeInterceptor localeChangeInterceptor(){
LocaleChangeInterceptor changeInterceptor=new LocaleChangeInterceptor();
changeInterceptor.setParamName("locale");
return changeInterceptor;
}
#Bean
public ControllerClassNameHandlerMapping nameHandlerMapping(){
ControllerClassNameHandlerMapping controller=new ControllerClassNameHandlerMapping();
Object[] obj={localeChangeInterceptor()};
controller.setInterceptors(obj);
return controller;
}*/
}

How to test the destruction of session scoped beans in jUnit?

I have a session scoped bean in which I hold some user data and want to write a unit test to ensure that it remains session scoped.
I want to mock the starting and the ending of a session in jUnit and compare the values of the session scoped bean.
For now I have the following (rough drafts) of the unit test:
I use a custom context loader to register the session scope.
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.test.context.support.GenericXmlContextLoader;
import org.springframework.web.context.request.SessionScope;
public class SessionScopedGenericXmlContextLoader extends GenericXmlContextLoader {
#Override
protected void customizeBeanFactory(final DefaultListableBeanFactory beanFactory) {
beanFactory.registerScope("session", new SessionScope());
super.customizeBeanFactory(beanFactory);
}
}
The unit test:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(loader = SessionScopedGenericXmlContextLoader.class,
locations = { "classpath:/test-applicationContext.xml","classpath:/cache-config.xml" })
public class CachingTest extends AbstractJUnit4SpringContextTests {
// Session scoping
protected MockHttpSession session;
protected MockHttpServletRequest request;
ApplicationContext ctx;
CacheManager cacheManager;
Cache accountSettingsCache;
#Autowired
#Qualifier("cachedMethods")
DummyCacheMethods methods;
#Autowired
private SecurityContextHolder contextHolder;
protected void startRequest() {
request = new MockHttpServletRequest();
request.setSession(session);
RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(request));
}
protected void endRequest() {
((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).requestCompleted();
RequestContextHolder.resetRequestAttributes();
request = null;
}
protected void startSession() {
session = new MockHttpSession();
}
protected void endSession() {
session.clearAttributes();
session = null;
}
#Before
public void constructSession() {
ctx = applicationContext;
cacheManager = (CacheManager) ctx.getBean("cacheManager");
accountSettingsCache = cacheManager.getCache("accountSettingsCache");
startRequest();
startSession();
}
#After
public void sessionClean() {
endRequest();
endSession();
contextHolder.clearContext();
}
#Test
// #DirtiesContext
public void checkSession1() {
final Authentication authentication = new Authentication() {
public String getName() {
return "Johny";
}
public void setAuthenticated(final boolean isAuthenticated) throws IllegalArgumentException {
}
public boolean isAuthenticated() {
return true;
}
public Object getPrincipal() {
final CustomUserDetails pr = new CustomUserDetails();
pr.setUsername("Johny");
return pr;
}
public Object getDetails() {
return null;
}
public Object getCredentials() {
return null;
}
public Collection<GrantedAuthority> getAuthorities() {
return null;
}
};
contextHolder.getContext().setAuthentication(authentication);
assertTrue(methods.getCurrentUserName().equals(accountSettingsCache.get("currentUser").get()));
assertTrue(methods.getCurrentUserName().equals(((CustomUserDetails) authentication.getPrincipal()).getUsername()));
}
#Test
// #DirtiesContext
public void testSession2() {
final Authentication authentication2 = new Authentication() {
public String getName() {
return "James";
}
public void setAuthenticated(final boolean isAuthenticated) throws IllegalArgumentException {
}
public boolean isAuthenticated() {
return true;
}
public Object getPrincipal() {
final CustomUserDetails pr = new CustomUserDetails();
pr.setUsername("James");
return pr;
}
public Object getDetails() {
return null;
}
public Object getCredentials() {
return null;
}
public Collection<GrantedAuthority> getAuthorities() {
return null;
}
};
SecurityContextHolder.setContext(contextHolder.getContext());
SecurityContextHolder.clearContext();
SecurityContextHolder.getContext().setAuthentication(authentication2);
assertTrue(methods.getCurrentUserName().equals(accountSettingsCache.get("currentUser").get()));
assertTrue(methods.getCurrentUserName().equals(((CustomUserDetails) authentication2.getPrincipal()).getUsername()));
}
#Test
public void testWiring() {
assertTrue("cacheManager is null", cacheManager != null);
assertTrue("accountSettingsCache is null", accountSettingsCache != null);
}
}
And the relevant function from DummyCachedMethods is:
#Cacheable(value = { "accountSettingsCache" }, key = "new String(\"currentUser\")")
public String getCurrentUserName() {
return WebUtils.getCurrentUser().getUsername();
}
WebUtils.getCurrentUser() just returns the current Principal from the SecurityContext.
But this does not work, the test does not pass at this line:
assertTrue(methods.getCurrentUserName().equals(((CustomUserDetails) authentication2.getPrincipal()).getUsername()));
Since the method call is cached, it still returns Johnny instead of James.
My cache related beans are:
<bean id="accountSettingsCache" class="org.springframework.cache.concurrent.ConcurrentMapCache" scope="session">
<constructor-arg>
<value>accountSettingsCache</value>
</constructor-arg>
<aop:scoped-proxy proxy-target-class="false" />
</bean>
<bean id="defaultCache" class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean" p:name="defaultCache" />
<bean id="cacheManager" class="my.package.DynamicCacheManager" scope="session">
<property name="caches">
<set>
<ref bean="accountSettingsCache" />
<ref bean="defaultCache" />
</set>
</property>
<aop:scoped-proxy />
</bean>
accountSettingsCache is session scoped, I want to start a new session and destroy it.
It does pass if I uncomment the DirtiesContext annotations, but I guess it's not what I want.
What am I missing ?

Resources