Issue
I'm trying to modify a wicket application to store the session in redis via spring-session. The session is showing up in redis, but I've run into a problem that whenever the application makes a standard wicket ajax call, the response from wicket includes an Ajax-Location header that is interpreted by wicket-ajax-jquery.js triggering a page redirect. But this only happens AFTER the first ajax call has been successful. For example, the first ajax call may look like this:
http://host:port/context/help/admin?0-1.IBehaviorListener.0-smartTable-tableArea-records-0-row-detailToggleCell-detailToggleLink&_=1636756805561
and the response headers do NOT include Ajax-Location. And then later, the next ajax call may look like this:
http://host:port/context/help/admin?1-1.IBehaviorListener.0-smartTable-tableArea-records-0-row-detailToggleCell-detailToggleLink&_=1636756906417
But the response header now includes this:
Ajax-Location: ./admin?2
and instead of just doing the ajax update to the page, the entire page redirects to the URL specified in that header because of code in src/main/java/org/apache/wicket/ajax/res/js/wicket-ajax-jquery.js
Digging down into the wicket-core code using the debugger, consider this where it doesn't produce the Ajax-Location header and works properly:
Step completed: "thread=ba6f07:3", org.apache.wicket.core.request.handler.ListenerInterfaceRequestHandler.respond(), line=197 bci=169
ba6f07:3[1] print canCallListenerInterfaceAfterExpiry
canCallListenerInterfaceAfterExpiry = false
ba6f07:3[1] print freshPage
freshPage = false
ba6f07:3[1] print isStateless
isStateless = false
ba6f07:3[1] print component
component = "[AjaxLink [Component id = detailToggleLink]]"
and then compare with this where it DOES produce an Ajax-Location header and doesn't work properly:
Breakpoint hit: "thread=ba6f07:7", org.apache.wicket.core.request.handler.ListenerInterfaceRequestHandler.respond(), line=197 bci=169
ba6f07:7[1] print canCallListenerInterfaceAfterExpiry
canCallListenerInterfaceAfterExpiry = false
ba6f07:7[1] print freshPage
freshPage = true
ba6f07:7[1] print isStateless
isStateless = false
ba6f07:7[1] print component
component = null
The difference being that when it doesn't work, freshPage is true and component is null.
Note: this pattern is fully functional in another similar application that I have and I’ve spent some time comparing the two. Clearly, something is missing from the original application in the app that I’m working on but I haven’t been able to identify it yet.
My redis http session config class looks like this:
import javax.annotation.PostConstruct;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
import org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration;
import org.springframework.session.web.http.CookieHttpSessionStrategy;
import org.springframework.session.web.http.DefaultCookieSerializer;
@Configuration
@EnableRedisHttpSession
public class MyRedisHttpSessionConfig extends RedisHttpSessionConfiguration
{
private JedisConnectionFactory connectionFactory;
@PostConstruct
public void init()
{
CookieHttpSessionStrategy strategy = new CookieHttpSessionStrategy();
DefaultCookieSerializer cookieSerializer = new DefaultCookieSerializer();
cookieSerializer.setCookieName( "SESSION" );
strategy.setCookieSerializer(cookieSerializer);
setHttpSessionStrategy( strategy );
}
@Bean
public JedisConnectionFactory connectionFactory() throws Exception
{
return connectionFactory;
}
public void setConnectionFactory( JedisConnectionFactory connectionFactory )
{
this.connectionFactory = connectionFactory;
}
}
my web.xml has this:
...
<filter>
<filter-name>requestLoggingFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter>
<filter-name>springSessionRepositoryFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter>
<filter-name>myApplicationWicketFilter</filter-name>
<filter-class>org.apache.wicket.protocol.http.WicketFilter</filter-class>
<init-param>
<param-name>applicationFactoryClassName</param-name>
<param-value>org.apache.wicket.spring.SpringWebApplicationFactory</param-value>
</init-param>
<init-param>
<param-name>filterMappingUrlPattern</param-name>
<param-value>/*</param-value>
</init-param>
</filter>
...
<filter-mapping>
<filter-name>springSessionRepositoryFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
...
<filter-mapping>
<filter-name>ariesApplicationWicketFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>ERROR</dispatcher>
</filter-mapping>
...
and my spring beans config file has this:
...
<!-- The RedisHttpSessionConfiguration creates an http Filter bean with name "springSessionRepositoryFilter" which is referenced in web.xml -->
<context:annotation-config/>
<util:constant static-field="org.springframework.session.data.redis.config.ConfigureRedisAction.NO_OP"/>
<bean class="MyRedisHttpSessionConfig">
<property name="connectionFactory" ref="webTierRedisConnectionFactory"/>
</bean>
<bean id="webTierRedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
<property name="hostName" value="${service-tier:redisSentinelMasterName}"/>
<property name="port" value="${service-tier:redisSentinelHostPortCsv}"/>
<property name="usePool" value="true"/>
<property name="poolConfig">
<bean class="redis.clients.jedis.JedisPoolConfig">
<property name="maxWaitMillis" value="5000"/>
<property name="maxTotal" value="50"/>
<property name="maxIdle" value="5"/>
<property name="minIdle" value="1"/>
<property name="testWhileIdle" value="true"/>
<property name="timeBetweenEvictionRunsMillis" value="60000"/>
<property name="numTestsPerEvictionRun" value="10"/>
</bean>
</property>
</bean>
...
Ivy Dependencies include:
<!-- these are for redis httpsession -->
<dependency org="redis.clients" name="jedis" rev="2.8.1"/>
<dependency org="org.springframework.data" name="spring-data-redis" rev="1.7.4.RELEASE"/>
<dependency org="org.springframework.data" name="spring-data-keyvalue" rev="1.1.4.RELEASE"/>
<dependency org="org.springframework.session" name="spring-session" rev="1.2.2.RELEASE"/>
and wicket 7.5.0 and spring 4.2.8 stuff.
Anybody have any insights on what might be going on? Why after putting the session into redis (which it is showing up there, I see it (via redis-cli and keys and dump commands), most ajax calls are triggering full page redirects due to response headers from the ajax call including Ajax-Location?
Solution
This turned out to be caused by a custom org.apache.wicket.IPageManagerProvider implementation which needed to be replaced with a version that was compatible with redis.
Answered By - Diane Megan Harris
Answer Checked By - Timothy Miller (JavaFixing Admin)