Spring Mobile - Interceptor not applied? Device is null
java, spring, spring-mobile, spring-mvc
Solution
I've had a look at this and managed to get it to work myself. So I have a few comments.
1a) I don't think both the Filter and the Interceptor are required. I've just used the Filter and that was enough.
1b) The Interceptor (if used) should be configured in a `DispatcherServlet` xml config file. You look like you are using Struts from the use of `ActionSupport`, is this correct? If so, you (probably) won't have a `DispatcherServlet` and therefore I don't think this config will work as expected. I think that's why you're getting the stack trace.
2) I would add a breakpoint to `org.springframework.mobile.device.DeviceResolverRequestFilter.doFilterInternal` to make sure it's being executed.
3) I would check that Struts isn't doing something 'funny' with the `ServletRequest`, and hiding the "currentDevice" request attribute from you somehow. In fact, I would port your code to vanilla Spring if possible.
4) Maybe you could use `ServletActionContext.getRequest` in your `execute` method and see if that works, and/or compare the returned request to that set in `setServletRequest`.
Using Spring MVC, this is what works for me. My project is called spring-mobile-test, and "spring-mobile-test" is its context root:
web.xml:
<?xml version="1.0" encoding="utf-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<filter>
<filter-name>deviceResolverRequestFilter</filter-name>
<filter-class>org.springframework.mobile.device.DeviceResolverRequestFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>deviceResolverRequestFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<servlet>
<servlet-name>mvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>mvc</servlet-name>
<url-pattern>/mvc/*</url-pattern>
</servlet-mapping>
</web-app>
applicationContext.xml is empty.
mvc-servlet.xml:
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd">
<context:component-scan base-package="temp" />
</beans>
temp.TestController:
package temp;
import javax.servlet.http.HttpServletRequest;
import org.apache.log4j.Logger;
import org.springframework.mobile.device.Device;
import org.springframework.mobile.device.DeviceUtils;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class TestController {
private static final Logger logger = Logger.getLogger(TestController.class);
@RequestMapping("/")
public @ResponseBody
String home(HttpServletRequest req) {
Device device = DeviceUtils.getCurrentDevice(req);
String msg = "";
if (device.isMobile()) {
msg = "Hello mobile user!";
} else {
msg = "Hello desktop user!";
}
logger.info(msg);
return msg;
}
}
The browser shows the following text when I browse to the URL `http://localhost/spring-mobile-test/mvc/`:
Hello desktop user!
Problem
I'm experimenting with Spring Mobile but I can't seem to get the basic example working. I have a feeling I'm missing something stupidly simple but I can't figure out what it is. Here is what I have in place... In web.xml ``` <context-param> <param-name>contextConfigLocation</param-name> <param-value> /WEB-INF/applicationContext.xml </param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <filter> <filter-name>deviceResolverRequestFilter</filter-name> <filter-class>org.springframework.mobile.device.DeviceResolverRequestFilter</filter-class> </filter> <filter-mapping> <filter-name>deviceResolverRequestFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> ``` In applicationContext.xml ``` <beans:beans xmlns="http://www.springframework.org/schema/mvc" xmlns:beans="http://www.springframework.org/schema/beans" xmlns:device="http://www.springframework.org/schema/mobile/device" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/mobile/device http://www.springframework.org/schema/mobile/device/spring-mobile-device-1.0.xsd"> <!-- Interceptors that execute common control logic across multiple requests --> <interceptors> <!-- Detects the client's Device --> <beans:bean class="org.springframework.mobile.device.DeviceResolverHandlerInterceptor" /> </interceptors> </beans:beans> ``` In my Java class: ``` public class TestAction extends ActionSupport implements ServletRequestAware { // So that we can lookup the current Device private HttpServletRequest request; public void setServletRequest(HttpServletRequest request) { this.request = request; } @Override public String execute() { Device currentDevice = DeviceUtils.getCurrentDevice(request); if (currentDevice.isMobile()) // <-- fails here with NPE ``` Why is the device not set and resulting as null? EDIT: Log files seem to indicate a problem with setting the interceptor but I'm still not sure where I went wrong. 2012-05-29 09:36:36,696 DEBUG [ConstructorResolver.java:201] : Ignoring constructor [public org.springframework.web.servlet.handler.MappedInterceptor(java.lang.String[],org.springframework.web.context.request.WebRequestInterceptor)] of bean 'org.springframework.web.servlet.handler.MappedInterceptor#0': org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'org.springframework.web.servlet.handler.MappedInterceptor#0': Unsatisfied dependency expressed through constructor argument with index 1 of type [org.springframework.web.context.request.WebRequestInterceptor]: Could not convert constructor argument value of type [org.springframework.mobile.device.DeviceResolverHandlerInterceptor] to required type [org.springframework.web.context.request.WebRequestInterceptor]: Failed to convert value of type 'org.springframework.mobile.device.DeviceResolverHandlerInterceptor' to required type 'org.springframework.web.context.request.WebRequestInterceptor'; nested exception is java.lang.IllegalStateException: Cannot convert value of type [org.springframework.mobile.device.DeviceResolverHandlerInterceptor] to required type [org.springframework.web.context.request.WebRequestInterceptor]: no matching editors or conversion strategy found