I am trying to use Spring MVC 3 and Hibernate 4.1 to get declarative transaction management, providing an open session for the entire length of the request. I only have one "manager" layer for data access, which directly uses the session. This is annotated with #Transactional, and the session closes upon exiting this layer and I get a lazy load exception in the controller.
So I added the OpenSessionInViewFilter filter, and the logs state that this filter is correctly configured and they also show this filter opening a session. Unfortunately, my manager layer opens and closes its own session anyway, just as before, and I get the same lazy load exception in the controller.
Edit: noted that in my log, I get the filter session open, HibernateTransactionManager session open, HibernateTransactionManager session closed, lazy load exception, then filter session closed. So I know that there is an open session somewhere during the lazy load exception, but the object was loaded in a transaction associated with the other, closed session.
I thought that removing #Transactional from the manager class would remove session management from that layer and let the filter do its job, but then the sessionFactory().getCurrentSession() simply gets a closed session; it doesn't have access to the filter's provided open session.
How can I get access to the OpenSessionInViewFilter's clearly open session?
springapp-servlet.xml
<beans ..
<context:component-scan base-package="springapp.web" />
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping"/>
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"/>
<!-- Enables the Spring MVC #Controller programming model -->
<mvc:annotation-driven />
<bean id="userManager" class="springapp.service.SimpleUserManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
</bean>
<import resource="hibernate-context.xml" />
</beans>
web.xml
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/springapp-servlet.xml</param-value>
</context-param>
<filter>
<filter-name>openSessionInView</filter-name>
<filter-class>org.springframework.orm.hibernate4.support.OpenSessionInViewFilter</filter-class>
<init-param>
<param-name>sessionFactoryBeanName</param-name>
<param-value>sessionFactory</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>openSessionInView</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<servlet>
<servlet-name>springapp</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>springapp</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
hibernate-context.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans ..
">
<context:property-placeholder location="/WEB-INF/hibernate.properties" />
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${hibernate.connection.driver_class}" />
<property name="url" value="${hibernate.connection.url}" />
<property name="username" value="${hibernate.connection.username}" />
<property name="password" value="${hibernate.connection.password}" />
</bean>
<bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<tx:annotation-driven />
<bean id="transactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor">
<property name="transactionManager" ref="transactionManager" />
<property name="transactionAttributeSource">
<bean class="org.springframework.transaction.annotation.AnnotationTransactionAttributeSource" />
</property>
</bean>
<bean id="managerTemplate" abstract="true" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="interceptorNames">
<list>
<value>transactionInterceptor</value>
</list>
</property>
</bean>
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="packagesToScan" value="databeans" />
<property name="dataSource" ref="dataSource" />
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">${hibernate.dialect}</prop>
</props>
</property>
</bean>
</beans>
manager.java
#Service("userManager")
#Transactional
public class SimpleUserManager implements UserManager {
#Autowired
private SessionFactory sessionFactory;
public void setSessionFactory(SessionFactory sessionFactory){
this.sessionFactory = sessionFactory;
}
..
controller.java
#Controller
#RequestMapping("/users")
public class UserController {
#Autowired
private UserManager userManager;
..
and my exception, which occurs in the controller, on the first property read of an object loaded in the manager class and passed to that controller:
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.hibernate.LazyInitializationException: could not initialize proxy - no Session
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:894)
org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:778)
javax.servlet.http.HttpServlet.service(HttpServlet.java:617)
javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
org.springframework.orm.hibernate4.support.OpenSessionInViewFilter.doFilterInternal(OpenSessionInViewFilter.java:119)
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76)
I've been researching this for hours.. all I can find is a mix of solutions using hibernate 3 and grainy explanations on hibernate 4 session/transaction theory without examples.
Any thoughts?
UPDATE: FIXED!
Found this:
Spring MVC OpenSessionInViewInterceptor Not Working
which unfortunately has not helped the OP there, but it helped me. Notably, this:
"it will be better to not import applicationContext.xml in dispatcher-servlet.xml, instead load it using a ContextLoaderListener"
which, applied to my configuration, is exactly everything I have posted above, but instead of
<import resource="hibernate-context.xml" />
in my springapp-servlet.xml, I modified my ContextLoaderListener to:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/springapp-servlet.xml, /WEB-INF/hibernate-context.xml</param-value>
</context-param>
and that was all it took for
sessionFactory.getCurrentSession()
to return to me the OSIV-created session. And, in fact, the second session that was being created is no longer, as I found these lovely little log lines:
2012-10-04 14:43:48,743 DEBUG http-8080-1 org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'transactionManager'
2012-10-04 14:43:48,743 TRACE http-8080-1 org.springframework.transaction.support.TransactionSynchronizationManager - Retrieved value [org.springframework.orm.hibernate4.SessionHolder#4146c5c0] for key [org.hibernate.internal.SessionFactoryImpl#12542011] bound to thread [http-8080-1]
2012-10-04 14:43:48,743 DEBUG http-8080-1 org.springframework.orm.hibernate4.HibernateTransactionManager - Found thread-bound Session [SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=[] updates=[] deletions=[] collectionCreations=[] collectionRemovals=[] collectionUpdates=[] unresolvedInsertDependencies=UnresolvedEntityInsertActions[]])] for Hibernate transaction
2012-10-04 14:43:48,743 DEBUG http-8080-1 org.springframework.orm.hibernate4.HibernateTransactionManager - Creating new transaction with name [springapp.service.SimpleUserManager.find]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
which show that, outside of my #Transactional service layer, the OSIV-created session was alive and well, and a new transaction was simply started for it.
Hope this helps someone else. I don't even want to think about how many hours I spent working on it.
Related
Versions (SpringBoot is not involved):
Spring: 5.2.16
web-app / servlet API: 4.0
JUnit: 5.8
Spring MVC Testing is not working for controller endpoint that returns ResponseEntity<ReturnStatus>, where ReturnStatus is a POJO with appropriate getters/setters. The exception triggered indicates that JSON conversion is not working for ReturnStatus. My research indicates that the annotation-based Java configuration for the WebApplicationContext is not loaded (and therefore the Jackson JSON converter is not recognized). Curiously, in a non-testing deployment in Tomcat, the controller endpoint works fine, presumably because the web.xml in the war-file is parsed by Tomcat.
QUESTION:
How can I adjust the setup for Spring MVC Test for this application so that the annotation-based Java configuration for the WebApplicationContext is properly loaded? Can this, for example, be done explicitly in the endpoint-test logic (ie, the JUnit test)?
Exception:
14:33:57,765 WARN DefaultHandlerExceptionResolver:199 - Resolved [org.springframework.http.converter.HttpMessageNotWritableException: No converter for [class com.acme.myapp.io.ReturnStatus] with preset Content-Type 'null']
14:33:57,765 DEBUG TestDispatcherServlet:1131 - Completed 500 INTERNAL_SERVER_ERROR
The Spring MVC app incorporates the following configurations:
test-context.xml, which houses Spring bean-configuration for access to data store:
web.xml, which declares and maps the DispatcherServlet with relevant setup for WebApplicationContext.
Annotation-based configuration in Java implementation of WebMvcConfigurer.
Relevant excerpt from test-context.xml:
<context:component-scan base-package="com.acme.myapp"/>
<jpa:repositories base-package="com.acme.myapp.repos"/>
<context:property-placeholder location="classpath:/application.properties" />
<!-- Data persistence configuration -->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="showSql" value="${db.showSql}" />
<property name="databasePlatform" value="${db.dialect}" />
<property name="generateDdl" value="${db.generateDdl}" />
</bean>
</property>
<property name="packagesToScan">
<list>
<value>com.acme.myapp.dao</value>
</list>
</property>
</bean>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${db.driver}" />
<property name="url" value="${db.url}" />
<property name="username" value="${db.user}" />
<property name="password" value="${db.pass}" />
<property name="initialSize" value="2" />
<property name="maxActive" value="5" />
<property name="accessToUnderlyingConnectionAllowed" value="true"/>
</bean>
<!-- Set JVM system properties here. We do this principally for hibernate logging. -->
<bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetObject">
<bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetClass" value="java.lang.System" />
<property name="targetMethod" value="getProperties" />
</bean>
</property>
<property name="targetMethod" value="putAll" />
<property name="arguments">
<util:properties>
<prop key="org.jboss.logging.provider">slf4j</prop>
</util:properties>
</property>
</bean>
Relevant excerpt from web.xml (where application-context.xml is our production version of test-context.xml):
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:application-context.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>central-dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
<init-param>
<param-name>contextClass</param-name>
<param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
</init-param>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>com.acme.myapp.MyAppWebAppConfig</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>central-dispatcher</servlet-name>
<url-pattern>/api/*</url-pattern>
</servlet-mapping>
Excerpt from Java implementation of WebMvcConfigurer (ie, where we incorporate Jackson JSON converter):
#EnableWebMvc
#Configuration
#ComponentScan(basePackages = { "com.acme.myapp.controllers" })
public class MyAppWebAppConfig implements WebMvcConfigurer
{
private static final Logger logger = LoggerFactory.getLogger(MyAppWebAppConfig.class);
#Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters)
{
logger.debug("extendMessageConverters ...");
converters.add(new StringHttpMessageConverter());
converters.add(new MappingJackson2HttpMessageConverter(new MyAppObjectMapper()));
}
}
The controller endpoint looks like this (where the root is at /patients):
#RequestMapping(value = "/{id}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<ReturnStatus> readPatient(
#PathVariable("id") long id
)
{
ReturnStatus returnStatus = new ReturnStatus();
returnStatus.setVersionId("1.0");
...
return new ResponseEntity<ReturnStatus>(returnStatus, httpStatus);
}
Using JUnit5 and MockMvc, the endpoint-test looks like this:
#SpringJUnitWebConfig(locations={"classpath:test-context.xml"})
public class PatientControllerTest
{
private MockMvc mockMvc;
#BeforeEach
public void setup(WebApplicationContext wac) {
this.mockMvc = webAppContextSetup(wac).build();
}
#Test
#DisplayName("Read Patient from /patients API.")
public void testReadPatient()
{
try {
mockMvc.perform(get("/patients/1").accept(MediaType.APPLICATION_JSON_VALUE))
.andDo(print())
.andExpect(status().isOk());
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
Thanks!
Here are some options, possibly not exhaustive:
Per earlier comment, we can simply use <mvc:annotation-driven> directive in test-context.xml. For example:
<bean id="myappObjectMapper" class="com.acme.myapp.MyAppObjectMapper"/>
<mvc:annotation-driven>
<mvc:message-converters register-defaults="true">
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<constructor-arg ref="myappObjectMapper"/>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
Effectively, this directive obviates the need for loading MyAppWebAppConfig, as <mvc:annotation-driven> in fact is the XML-equivalent of the annotation #EnableWebMvc in Java.
Implement WebApplicationInitializer so that effectively does in Java what we configure into web.xml. For example:
public class MyAppWebApplicationInitializer implements WebApplicationInitializer
{
#Override
public void onStartup(ServletContext container)
{
XmlWebApplicationContext appCtx = new XmlWebApplicationContext();
appCtx.setConfigLocation("classpath:application-context.xml");
container.addListener(new ContextLoaderListener(appCtx));
AnnotationConfigWebApplicationContext dispatcherCtx = new AnnotationConfigWebApplicationContext();
dispatcherCtx.register(MyAppWebAppConfig.class);
ServletRegistration.Dynamic registration = container.addServlet("central-dispatcher", new DispatcherServlet(dispatcherCtx));
registration.setLoadOnStartup(1);
registration.addMapping("/api/*");
}
}
For this solution, we expunge web.xml from the project; possibly we should parameterize the reference to application-context.xml as well.
Note that when I run JUnit5 tests, it appears that Spring does not instance MyAppWebApplicationInitializer, and that instead, the Spring context loaded for JUnit5 is the one referenced by the #SpringJUnitWebConfig annotation. I therefore recommend co-locating test-related configuration with test-context.xml, and reserving WebApplicationInitializer for production.
I'm sure there are other options, but I've only explored these two approaches.
I'm trying to develop an application with spring security login functionality while user session is distributed using apache Ignite.
Server: Apache Tomcat 8
Spring version: 4.2.2.RELEASE
Ignite version: 2.1.0
I have two errors in my application.
Logging an exception when logging out from the application. Apart from that session invalidation is done as expected.
12-Aug-2017 14:09:01.580 SEVERE [http-nio-8080-exec-2] org.apache.ignite.logger.java.JavaLogger.error Failed to update web session: null
java.lang.NullPointerException
at org.apache.ignite.cache.websession.WebSessionFilter$RequestWrapperV2.getSession(WebSessionFilter.java:1001)
at org.apache.ignite.cache.websession.WebSessionFilter.doFilterV2(WebSessionFilter.java:564)
at org.apache.ignite.cache.websession.WebSessionFilter.doFilterDispatch(WebSessionFilter.java:407)
at org.apache.ignite.cache.websession.WebSessionFilter.doFilter(WebSessionFilter.java:383)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:478)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:80)
at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:624)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:799)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:861)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1455)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
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:748)
When two tomcat servers with same application deployments started up in two different ports, can access login page only from one server.
(If I accessed login page from first server page is loaded fine as expected. But if I tried to access login page from second server again it gives error. However once logged in application worked as expected and could access distributed session from both servers.
First access
Second access from another server
My configuration files are as follows.
Spring context configuration
<beans xmlns.... >
<context:component-scan base-package="test.ignite.spring"/>
<mvc:annotation-driven/>
<context:property-placeholder location="classpath:system.properties" ignore-resource-not-found="true" ignore-unresolvable="true"/>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/pages/"/>
<property name="suffix" value=".jsp"/>
</bean>
<mvc:interceptors>
<mvc:interceptor>
<!-- Cache of HTML pages -->
<mvc:mapping path="/**"/>
<bean class="org.springframework.web.servlet.mvc.WebContentInterceptor">
<property name="cacheSeconds" value="0"/>
</bean>
</mvc:interceptor>
</mvc:interceptors>
</beans>
Login Controller
#Controller
public class LoginController {
#RequestMapping(value = {"login", "/"})
public String login() {
try {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (!(auth instanceof AnonymousAuthenticationToken)) {
return "home";
}
return "login";
} catch (Exception e) {
return "redirect:/error/500";
}
}
#RequestMapping(value = "/home")
public String home() {
return "home";
}
}
----UPDATED----
Ignite configuration (whole file content)
<beans xmlns.... >
<bean abstract="true" id="ignite.cfg" class="org.apache.ignite.configuration.IgniteConfiguration">
<!-- Set to true to enable distributed class loading for examples, default is false. -->
<property name="peerClassLoadingEnabled" value="true"/>
<property name="cacheConfiguration">
<list>
<bean class="org.apache.ignite.configuration.CacheConfiguration">
<property name="name" value="example"/>
<property name="cacheMode" value="PARTITIONED"/>
</bean>
</list>
</property>
<!-- Enable task execution events for examples. -->
<property name="includeEventTypes">
<list>
<!--Task execution events-->
<util:constant static-field="org.apache.ignite.events.EventType.EVT_TASK_STARTED"/>
<util:constant static-field="org.apache.ignite.events.EventType.EVT_TASK_FINISHED"/>
<util:constant static-field="org.apache.ignite.events.EventType.EVT_TASK_FAILED"/>
<util:constant static-field="org.apache.ignite.events.EventType.EVT_TASK_TIMEDOUT"/>
<util:constant static-field="org.apache.ignite.events.EventType.EVT_TASK_SESSION_ATTR_SET"/>
<util:constant static-field="org.apache.ignite.events.EventType.EVT_TASK_REDUCED"/>
<!--Cache events-->
<util:constant static-field="org.apache.ignite.events.EventType.EVT_CACHE_OBJECT_PUT"/>
<util:constant static-field="org.apache.ignite.events.EventType.EVT_CACHE_OBJECT_READ"/>
<util:constant static-field="org.apache.ignite.events.EventType.EVT_CACHE_OBJECT_REMOVED"/>
</list>
</property>
<!-- Explicitly configure TCP discovery SPI to provide list of initial nodes. -->
<property name="discoverySpi">
<bean class="org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi">
<property name="ipFinder">
<!--
Ignite provides several options for automatic discovery that can be used
instead os static IP based discovery. For information on all options refer
to our documentation: http://apacheignite.readme.io/docs/cluster-config
-->
<!-- Uncomment static IP finder to enable static-based discovery of initial nodes. -->
<!--<bean class="org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder">-->
<bean class="org.apache.ignite.spi.discovery.tcp.ipfinder.multicast.TcpDiscoveryMulticastIpFinder">
<property name="addresses">
<list>
<!-- In distributed environment, replace with actual host IP address. -->
<value>127.0.0.1:47500..47509</value>
</list>
</property>
</bean>
</property>
</bean>
</property>
</bean>
Spring security configuration
<beans:beans xmlns.... >
<http auto-config="true" create-session="always" use-expressions="true" >
<form-login login-page="/login" default-target-url="/home" authentication-failure-url="/login?error" username-parameter="username" password-parameter="password" always-use-default-target="true"/>
<logout invalidate-session="true" logout-success-url="/login" delete-cookies="JSESSIONID"/>
<session-management session-fixation-protection="newSession" invalid-session-url="/" session-authentication-error-url="/login">
<concurrency-control session-registry-alias="sessionRegistry" max-sessions="10" expired-url="/" error-if-maximum-exceeded="true"/>
</session-management>
<access-denied-handler error-page="/403"/>
</http>
<authentication-manager>
<authentication-provider user-service-ref="userDetailsService">
</authentication-provider>
</authentication-manager>
</beans:beans>
web.xml
<web-app xmlns... >
<listener>
<listener-class>org.apache.ignite.startup.servlet.ServletContextListenerStartup</listener-class>
</listener>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<listener>
<listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class>
</listener>
<filter>
<filter-name>IgniteWebSessionsFilter</filter-name>
<filter-class>org.apache.ignite.cache.websession.WebSessionFilter</filter-class>
</filter>
<!-- You can also specify a custom URL pattern. -->
<filter-mapping>
<filter-name>IgniteWebSessionsFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- Specify Ignite configuration (relative to META-INF folder or Ignite_HOME). -->
<context-param>
<param-name>IgniteConfigurationFilePath</param-name>
<param-value>example-ignite.xml</param-value>
</context-param>
<!-- Specify the name of Ignite cache for web sessions. -->
<context-param>
<param-name>IgniteWebSessionsCacheName</param-name>
<param-value>example</param-value>
</context-param>
<!--SERVLETS-->
<servlet>
<servlet-name>mvc-dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:mvc-dispatcher-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>mvc-dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:mvc-dispatcher-servlet.xml,
classpath:security-config.xml
</param-value>
</context-param>
</web-app>
I would be very thankful if you could provide any solution / idea to solve problem.
As both of your deployments are on localhost, they're going to share JSESSIONID cookie. So far so good.
However there seems to be a mismatch between Tomcat, Spring Security and Ignite which causes Spring Security to regard sessions coming from Ignite as invalid when they're anonymous (i.e. not logged on). I don't understand yet how this relates to your case where you're supposed to already be logged on.
You can probably use a workaround: Remove invalid-session-url="/" from your Spring Security configuration. This will prevent the redirect looping behavior. This will also cause users to be silently logged off instead to being guided to /login when their cookie expires.
I've deleted my previous answer as it was missing the point.
I use SpringSession+redis, it can save the session to redis,but I cant't get the session attribute the when i call "${sessionScope.user.name}" in jsp
this is web.xml
<filter>
<filter-name>springSessionRepositoryFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSessionRepositoryFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
this is applicationContext.xml
<!-- spring data redis -->
<bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
<property name="hostName" value="localhost"/>
<property name="port" value="6379"></property>
<property name="password" value="123456"></property>
<property name="database" value="0"></property>
<property name="usePool" value="true"></property>
</bean>
<bean class="org.springframework.data.redis.core.RedisTemplate" id="redisTemplate">
<property name="connectionFactory" ref="jedisConnectionFactory" />
</bean>
<!-- spring session -->
<context:annotation-config/>
<bean class="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration"/>
this is the controller:
#RequestMapping("/login")
public String login(String name, String pwd,HttpSession session){
User user=new User();
user.setName(name);
user.setPwd(pwd);
session.setAttribute("user",user);
return "forward:/success.jsp";
}
#RequestMapping("test")
public String test(HttpSession session){
//there,session.getAttribute("user")=null ???
return "forward:/test.jsp";
}
When i request the project, I can see the session has been saved in Redis
enter image description here
but when I request test.action ,It can't get the Seesion Attribute.
What's wrong?
I have a problem integrating this two technologies.
We have an GWT application integrated with Spring but, additionally to GWT application we need to display HTML files using Thymeleaf for special modules.
The GWT application works well but when I try to run it with Controllers and Thymeleaf enabled this fail.
I've added this to my web.xml to support the Spring Controllers use:
<servlet>
<servlet-name>appServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/conf/spring/controller-context.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>appServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
And my controller-context.xml has this beans:
<bean id="uiengineTemplateResolver"
class="org.thymeleaf.templateresolver.ServletContextTemplateResolver">
<property name="prefix" value="/uiengine/target/" />
<property name="suffix" value=".html" />
<property name="templateMode" value="HTML5" />
<property name="cacheable" value="false" />
<property name="characterEncoding" value="UTF-8" />
</bean>
<bean id="templateEngine" class="org.thymeleaf.spring3.SpringTemplateEngine">
<property name="templateResolvers">
<array>
<ref bean="uiengineTemplateResolver"/>
</array>
</property>
</bean>
And this at the end:
<context:component-scan base-package="com.enterprise.platform.*.gui" />
There is a way to integrate this two frameworks?
I'll appreciate your help!
Thanks.
I've resolved!
First you need to exclude the main HTML file for GWT from ThymeleafViewResolver so:
controller-context.xml
<bean id="viewResolver" class="org.thymeleaf.spring3.view.ThymeleafViewResolver">
<property name="templateEngine" ref="templateEngine" />
<property name="excludedViewNames">
<array>
<value>gwt-project.html</value>
</array>
</property>
</bean>
Allow the static access for the resources of GWT application:
<mvc:resources mapping="/gwt-project/**" location="/gwt-project/" />
Create a Spring Controller with access root method:
MyController.java
#Controller
public class MyController
{
#RequestMapping(value = "/{appBeanId}", method = RequestMethod.GET)
public String root(#PathVariable String appBeanId, Locale locale,
org.springframework.ui.Model model) {
....
}
}
That's it!.
I have made a project which is built on Maven+Spring 3.1+Spring MVC 3.1+Hibernate 4.1
In the transaction if I use the code:
Session session=sessionFactory.getCurrentSession();
session.save(user);
It gives the exception at the getCurrentSession():
SEVERE: Servlet.service() for servlet appServlet threw exception
org.hibernate.HibernateException: No Session found for current thread
at org.springframework.orm.hibernate4.SpringSessionContext.currentSession(SpringSessionContext.java:97)
at org.hibernate.internal.SessionFactoryImpl.getCurrentSession(SessionFactoryImpl.java:1024)
at com.company.cv.dao.UserDAO.saveUser(UserDAO.java:30)
at com.company.cv.service.UserService.saveUser(UserService.java:20)
at com.company.cv.HomeController.saveUser(HomeController.java:44)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:213)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:126)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:96)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:617)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:578)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:80)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:923)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:852)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:882)
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:789)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:637)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:298)
at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:857)
at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:588)
at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:489)
at java.lang.Thread.run(Thread.java:662)
But if I use openSession instead of getCurrentSession no exceptions and code transaction completes successfully:
Session session = sessionFactory.openSession();
session.save(user);
What is the reason?
Thanks for the help
My applicationContext.xml:
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/companycv" />
<property name="username" value="root" />
<property name="password" value="root" />
</bean>
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="annotatedClasses">
<list>
<value>com.company.cv.model.User</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.hbm2ddl.auto">create</prop>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
<prop key="hibernate.show_sql">true</prop>
</props>
</property>
</bean>
<bean id="txManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<tx:annotation-driven transaction-manager="txManager" />
servlet-context.xml:
<!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources directory -->
<resources mapping="/resources/**" location="/resources/" />
<!-- Resolves views selected for rendering by #Controllers to .jsp resources in the /WEB-INF/views directory -->
<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<beans:property name="prefix" value="/WEB-INF/views/" />
<beans:property name="suffix" value=".jsp" />
</beans:bean>
<context:component-scan base-package="com.company.cv" />
My service code:
#Service
public class UserService implements UserServiceIntf{
#Autowired
UserDAOIntf userDAO;
#Transactional
public void saveUser(User user) {
userDAO.saveUser(user);
}
}
My DAO code:
#Repository
public class UserDAO implements UserDAOIntf {
#Autowired
SessionFactory sessionFactory;
public void saveUser(User user) {
Session session=sessionFactory.getCurrentSession();
session.save(user);
}
}
You need add #Transactional annotation above your service method calling DAO method save. And since you have txManager defined in your app context you need not to manually manage the transactions.
One thing that I have observed you are doing wrong is that you are mixing the usage of Hibernate transaction API with the Spring managed transactions management abstraction. This is strictly not advised.
Session session=sessionFactory.getCurrentSession();
Transaction tx=session.beginTransaction(); // This is using Hibernate transaction API
session.save(user);
sessionFactory.getCurrentSession().save(user);
tx.commit();// This is using Hibernate transaction API
In the above code you are using hibernate transaction api to manage the transaction along with the Spring transaction management (when you annotate your Service method with transactional annotation)
You need to only use this
Session session=sessionFactory.getCurrentSession();
session.save(user);
sessionFactory.getCurrentSession().save(user);
Also as pointed out by others you have got duplicate datasource definitions listed.
And you mention that you can’t get sessionFactory which does not make sense as it is injected via Spring and you say that in the other case you have access to sessionFactory. There is no reason why this should happen.
UPDATE
So you are saying that you do have sessionFactory accessible in both cases but it’s not working in case of getCurrentSession. Having made all the changes suggested, the best advise will be to see what’s happening under the hood by enabling the Hibernate logging and Spring framework logging in log4j configuration ( if you are using log4j) track the relevant activity.
Your log4j configuration file would be something like this
# Direct log messages to a log file
log4j.appender.file=org.apache.log4j.RollingFileAppender
log4j.appender.file.File=<file-path>
log4j.appender.file.MaxFileSize=5MB
log4j.appender.file.MaxBackupIndex=1
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
# Root logger
log4j.rootLogger=INFO, file
# Hibernate and Spring logging
log4j.logger.org.hibernate=DEBUG
log4j.logger.org.springframework.transaction=DEBUG
This would give you a clear picture.
It was because of Hibernate4. If you look at the Hibernate4 DOC like I should have done in the first place ( :D ) Hibernate4 you have openSession every time. I think Hibernate guys made it so that noobies like me will be forced to use new session every time. Because session is not thread safe. FYI Hibernate openSession() vs getCurrentSession()