Spring seems to be all over the place today. Interesting projects just seem to keep coming out of their development machines. Hard to keep up. In my previous blog I had a look at the spring-social project. In this blog post I’ll have a go at the spring-mobile project. I’ll go through the things you have to do for configuration and I’ll have a go at creating a very basic sample application that you can pickup and play around with. I’ll even extend the framework just a little bit to implement a usecase I have that is not covered by the project itself.

Introduction

Spring mobile has it’s own home, you can find the homepage here. The project has a sample project as well. So why did I write this blog? Just to understand the project better and see how it fits in my personal projects. Maybe others will get a better understanding as well by reading this blog. Let me know if you find something interesting or have some improvements.

The sourcecode is available at Github : http://github.com/jettro/GridshoreSamples.git

The sample

I wanted to keep the sample as simple as possible to show how spring-mobile works. The sample must be complete enough to be able to explain the features that are important. Therefore I do not really create a functional thing. Just one page that shows whether you are using a mobile browser and some information about the browser you are using.

Simulation mobile platforms

Within Safari it is easy to pretend you are using another client than the safari browser that is installed. Use the developer menu bar item. Look at the following screendump.

Screen shot 2010-11-28 at 16.27.40.png

How does it work?

To understand how the resolvers can do what they do, resolve the client and learn about it capabilities, you need to know a bit about an html request. I take it that all the readers of my blog have the basic understanding of a html request header. Each request has a body and a header. The header contains some meta data about the client. To get the basic understanding, look at the code of the LiteDeviceResolver. This resolver uses a few items from the header like User-Agent, OperaMini and wap support to determine if a client is a mobile client. The class has a number of keywords that it checks for presence in the User-Agent string. In our case using the iPhone browser, there would be a match for the keyword phone.

Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_1 like Mac OS X; en-us) AppleWebKit/532.9 (KHTML, like Gecko) Version/4.0.5 Mobile/8B117 Safari/6531.22.7

Building the application

I use gradle to build the sample. Using the intellij and jetty plugin, gradle works like a charm. I do not want to go into to much details about gradle. You can find the file in the provided git repository.

Most important thing for the build configuration are the dependencies for spring mobile and an important library I use, wurfl. More on that later.


compile "org.springframework.mobile:spring-mobile-device:1.0.0.M1"
runtime "net.sourceforge.wurfl:wurfl:1.2"

Spring mobile configuration

Spring mobile is about resolving the client or device used to send a request. Therefore configuring spring-mobile is about configuring the device resolver. There are two options. The default makes use of the LiteDeviceResolver. The second option makes use of the WurflDeviceResolver. More on these resolvers follows. For both configurations you just need one bean. The following code blocks show both cases.

    <mvc:interceptors>
        <bean class="org.springframework.mobile.device.mvc.DeviceResolvingHandlerInterceptor"/>
    </mvc:interceptors>
    <mvc:interceptors>
        <bean class="org.springframework.mobile.device.mvc.DeviceResolvingHandlerInterceptor">
            <constructor-arg>
                <device:wurfl-device-resolver root-location="/WEB-INF/wurfl/wurfl-2.0.25.zip"/>
            </constructor-arg>
        </bean>
    </mvc:interceptors>

That is most of it already. Now you can resolve the different devices. There is one more thing if you want to actually do something with a the obtained device information. You might want to inject the Device into the Controller. From the reference documentation I copied the following class that registers WebArgumentResolver with the controller invocation. I do not really understand why this is not available as a provided bean. I think this will come, or maybe I misunderstood something. For completeness I’ll show you the code as well.

CustomWebArgumentResolverInstaller

@Component
public class CustomWebArgumentResolverInstaller {
    @Autowired
    public CustomWebArgumentResolverInstaller(AnnotationMethodHandlerAdapter controllerInvoker) {
        WebArgumentResolver[] resolvers = new WebArgumentResolver[1];
        resolvers[0] = new DeviceWebArgumentResolver();
        controllerInvoker.setCustomArgumentResolvers(resolvers);
    }
}

Device resolvers

Device resolvers use headers from the request to determine the Browser. The LiteDeviceResolver and with it the LiteDevice are created using the device resolving technique as copied from wordpress. It does not expose any capabilities of a mobile device, just that you are dealing with a mobile device.

The Device interface only exposes the isMobile method. You can also use the toString method. The following jsp shows some very basic lines of text.

<p>Is Mobile : <c:out value="${device.mobile}"/></p>
<p>Device:</p>
<c:out value="${device}"/>

Screen shot 2010-11-28 at 15.42.53.png

Spring mobile comes out of the box with support for wurfl, to be honest, I had never heard of it before. But it is a pretty extensive library that knows about a lot of phones and other mobile devices. It knows a lot more about the capabilities of your devise than the default LiteDevice. Spring mobile also provides namespace support for other device resolving strategies. The wurfl device resolver has the option to supply the data in a zip file and a patch in an xml file. The following image shows you part of the toString output of the same jsp. I did not use the patch functionality.

Screen shot 2010-11-28 at 16.06.24.png

The WurflDevice implementation exposes a lot more properties. You can ask for all capabilities and the markup. The following jsp code is used to show additional information.

<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core_rt" %>
<p>Is Mobile : <c:out value="${device.mobile}"/></p>
<h2>Basic Device info</h2>
<table>
    <tr>
        <td>id</td>
        <td><c:out value="${device.id}"/></td>
    </tr>
    <tr>
        <td>UserAgent</td>
        <td><c:out value="${device.userAgent}"/></td>
    </tr>
    <tr>
        <td>MarkUp</td>
        <td><c:out value="${device.markUp}"/></td>
    </tr>
</table>
<h2>Capabilities</h2>
<table>
    <tr>
        <th>key</th>
        <th>value</th>
    </tr>
    <c:forEach var="item" items="${device.capabilities}">
        <tr>
            <td><c:out value="${item.key}"/></td>
            <td><c:out value="${item.value}"/></td>
        </tr>
    </c:forEach>
</table>

The screen than looks like this

Screen shot 2010-11-28 at 16.36.48.png

iPad and other mobile devices with big screens

Some time a go I wanted to browse to the website of Schradinova using my iPad. I was redirected to the mobile website, but the mobile website was not available yet. There was no way to go to the actual site. By now the mobile site works, but still I cannot go to the original website using the iPad. Therefore I want to present the option to the user to explicitly go to the mobile site or to the normal site.

One way to accomplish this is by providing a mechanism to read a request parameter before doing the client resolving. Therefore I go back to my most simple example of showing the toString of the device. I have created a subclass of the spring provided DeviceResolvingHandlerInterceptor. My interceptor looks for the request param mobile. If found a dummy device is created with the isMobile method implemented. Might not be the nicest solution, but it does show the possibilities. The next code block shows the new HandlerInterceptor and the dummy device which is a direct copy of the LiteDevice.

public class WithOverruleDeviceResolvingHandlerInterceptor extends DeviceResolvingHandlerInterceptor {
    public static final String mobile = "mobile";

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (null != request.getParameter(mobile)) {
            boolean mobileOverruled = Boolean.parseBoolean(request.getParameter(mobile));
            Device overruledDevice;
            if (mobileOverruled) {
                overruledDevice = DummyDevice.MOBILE_INSTANCE;
            } else {
                overruledDevice = DummyDevice.NOT_MOBILE_INSTANCE;
            }
            request.setAttribute(CURRENT_DEVICE_ATTRIBUTE, overruledDevice);
            return true;
        } else {
            return super.preHandle(request, response, handler);
        }
    }
}

public class DummyDevice implements Device {

    public static final DummyDevice MOBILE_INSTANCE = new DummyDevice(true);

    public static final DummyDevice NOT_MOBILE_INSTANCE = new DummyDevice(false);

    public boolean isMobile() {
        return mobile;
    }

    public String toString() {
        StringBuilder builder = new StringBuilder();
        builder.append("[DummyDevice ");
        builder.append("mobile").append("=").append(isMobile());
        builder.append("]");
        return builder.toString();
    }

    private final boolean mobile;

    /**
     * Creates a GenericDevice.
     */
    private DummyDevice(boolean mobile) {
        this.mobile = mobile;
    }
}

I also add a link to the page to go to the mobile or the normal equivalent of the page. The following screendumps give you the different options using a mobile browser, a normal browser and using the explicit request for a mobile site or the normal site.

We start with the normal browser doing a normal request. Than we push the mobile link and after that the normal link. Look at the url bar, the is mobile and the first part of the description. Without an explicit preferred site it will show LiteDevice, in the other case it shows DummyDevice. Now three screendumps.

Screen shot 2010-11-28 at 20.52.31.png
Screen shot 2010-11-28 at 20.57.22.png
Screen shot 2010-11-28 at 20.57.54.png

If you really want to see it work for a mobile browser I’d urge you to checkout the sample and try it yourself 🙂

Last remarks

Well, that is it. You should be capable now of creating your own mobile site. Finally I want to mention that spring mobile also has an option to redirect a user with a mobile browser to a completely different site. Check the reference manual to see how that works.

If you got this far, thanks for reading. See you next time.

Playing with the spring mobile project
Tagged on:         

6 thoughts on “Playing with the spring mobile project

Comments are closed.