If you are like me and prefer code samples to theory, go ahead and download the example project straight away. Here is all you need to know:
It is a web application consisting of a single page only. It comes in two flavours, standard and mobile. I made them purposely very different. The mobile version runs on jQuery Mobile and makes thus it's standard counterpart (plain HTML only) look really poor.
In sync with the usual approach, the page reveals some details about the device it is viewed in. A mobile device is detected automatically and the mobile page is displayed by default. You always have a chance to switch to the other view by using a link on the respective page. To have some fun you might consider downloading the User Agent Switcher.
Finally, there is no XML configuration and no web.xml either. I tested it on Tomcat 7, but could not get it work with Jetty 7. You might need to check your web server documentation before you deploy.
That's pretty much all you need to know about the application. The rest of the post explains the example in detail, step-by-step.
When it comes to the device resolution Spring provides only the absolute minimum. Actually, it only tells whether the device is a mobile:
package org.springframework.mobile.device; public interface Device { boolean isMobile(); }To cater for a detailed device analysis Spring has partnered with the Wurfl project. There are two major benefits of Wurfl. First, it is for free. Secondly, it integrates well with Spring (although that might not necessarily be the case in the near future). Apart from that I could find another tool called simply Handset Detection. Since it is a commercial product I did not consider it for my little exercise.
Since I like to keep my code vendor-independent I created a wrapper around Spring's and Wurfl's features and hid it behind my very own interface:
package org.zezutom.springmobile.model; import javax.servlet.http.HttpServletRequest; /** * Resolves a device, based on a client request. */ public interface DeviceResolver { DeviceInfo resolveDevice(HttpServletRequest request); }Not to interfere with a Device as understood by Spring (and Wurfl, both using the same name:-) I call my interpretation of a device a DeviceInfo. It is not too far-fetched, as I am interested in a only a subset of information about the device rather than the device itself. However, my primary concern is distinguishing between mobile devices and the rest of the world and therefore I let the class implement the Spring's Device interface:
package org.zezutom.springmobile.model; import org.springframework.mobile.device.Device; import java.util.Collections; import java.util.Map; /** * Provides a vendor-independent description of a device. */ public class DeviceInfo implements Device { private boolean mobile; private boolean displayNormal; private String id; private String userAgent; private String markUp; private MapPlease note the boolean field called displayNormal. It has something to do with site preferences, which I am going to explain further down this post. Another field worth an explanation is the map of capabilities. This maps exactly to the Wurfl's amazing resolution capabilities. I use it to provide device details on the sample's front-end.capabilities; public DeviceInfo(boolean mobile) { this.mobile = mobile; } @Override public boolean isMobile() { return mobile; } // the usual getters and setters }
Let's move on to the actual device resolution. As I said my implementation is a mere wrapper around the underlying tools which, I hope, makes it really straightforward and easy to understand:
package org.zezutom.springmobile.model; import net.sourceforge.wurfl.core.Device; import net.sourceforge.wurfl.core.WURFLManager; import org.springframework.mobile.device.DeviceUtils; import org.springframework.mobile.device.site.SitePreference; import org.springframework.mobile.device.site.SitePreferenceUtils; import org.springframework.stereotype.Service; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; /** * Makes use of the WURFL tools to resolve the device. */ @Service public class WURFLDeviceResolver implements DeviceResolver { @Resource private WURFLManager manager; @Override public DeviceInfo resolveDevice(HttpServletRequest request) { // Spring caters for the elementary information // (is this a mobile?) boolean mobile = DeviceUtils.getCurrentDevice(request) .isMobile(); // Look for site preferences SitePreference sitePreference = SitePreferenceUtils.getCurrentSitePreference(request); // Decide the type of the device and how to display the device DeviceInfo deviceInfo = new DeviceInfo(mobile); deviceInfo .setDisplayNormal(SitePreference.NORMAL .equals(sitePreference)); // Other properties are resolved using the WURFL capabilities Device device = manager.getDeviceForRequest(request); deviceInfo.setId(device.getId()); deviceInfo.setCapabilities(device.getCapabilities()); deviceInfo.setMarkUp(device.getMarkUp().toString()); deviceInfo.setUserAgent(device.getUserAgent()); return deviceInfo; } }The last missing bit from the sample's bare bones is the controller. It utilizes the device resolver to get to the device details. It makes them available on the view and invokes the page appropriate for the obtained device. As one would expect, it is rather tiny:
package org.zezutom.springmobile.web; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; import org.zezutom.springmobile.model.DeviceInfo; import org.zezutom.springmobile.model.DeviceResolver; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; @Controller public class HomeController { @Resource private DeviceResolver deviceResolver; @RequestMapping("/index.htm") public String index(Model model, HttpServletRequest request) { DeviceInfo deviceInfo = updateModel(model, request); return getView(deviceInfo.isDisplayNormal()); } private DeviceInfo updateModel(Model model, HttpServletRequest request) { DeviceInfo deviceInfo = deviceResolver.resolveDevice(request); model.addAttribute("device", deviceInfo); return deviceInfo; } private String getView(boolean normal) { return normal ? "index" : "index-mobile"; } }To conclude on the device resolution, Spring tells you if a mobile is involved. Should you need more you are better off using a third-party tool, Wurfl probably being the most logical choice these days. I would like to make one last remark on this topic. You can actually make Spring pass the Device as an argument into your controller's method:
.. import org.springframework.mobile.device.Device; .. @Controller public class HomeController { @RequestMapping("/index.htm") public String index(Device device) { return device.isMobile() ? "mobile" : "normal"; } .. }Cool as it is, I could not get it work without resorting to XML configuration and thus excluded it from my solution. In reality this is certainly not a problem and it would be probably favoured over anything else.
The site preference management comes in handy when a user has a choice to say what website flavour do they prefer. A home-grown solution could look like that:
.. import org.springframework.mobile.device.Device; .. @Controller public class HomeController { @RequestMapping("/index.htm") public String index(boolean displayNormal) { return displayNormal ? "normal" : "mobile"; } .. }Nothing huge, but if you stick to naming conventions you can save yourself some coding and leave it up to Spring. Here is a snippet from a JSP page:
.. <a href="${currentUrl}?site_preference=mobile">Mobile</a><br /> <a href="${currentUrl}?site_preference=normal">Normal</a> ..An extra configuration needs to be done to turn the feature on. Once enabled, you are free to inspect the incoming HTTP request to learn what site is preferred. Reckon my device resolver earlier in this post:
.. import org.springframework.mobile.device.site.SitePreference; import org.springframework.mobile.device.site.SitePreferenceUtils; .. // Look for site preferences SitePreference sitePreference = SitePreferenceUtils.getCurrentSitePreference(request); ..For a maximum comfort, you can have the site preference passed as an argument into your controller's method. Again, yet another piece of configuration needs to be added to make it happen.
That pretty much concludes all I wanted to say. I rushed through two out of three major parts of the mobile framework, the third being the site switching. To me, Spring Mobile is not an extensive tool to learn. Even though it is not astonishingly rich in functionality, it works best when accompanied by a right stack of technologies. I see it as a good-to-have complement to the Spring MVC.
Download Source Code
Hi Tomas,
ReplyDeleteI have tried to run your sample app in tomcat 7 but its not running(If i verified in tomcat manager application is not started) could you please let us know which server we can run this sample application and procedure for running.
Hello,
Deletemy bad, I forgot to check documentation of the war plugin. By default, it fails if no web.xml is found. This is now fixed, enjoy.
Hi Tomas,
DeleteI added the web.xml and try to run this application still its not running could you please let me know the source location of fixed one(with web.xml).
Thanks,
Sadhana
Hi Sadhana,
Deleteinstead of adding the web.xml, which has no real use here, I fixed the configuration of the war plugin. You can review the committed change at: https://github.com/zezutom/Spring-Mobile-Demo/commit/cf6e3d27323a099df12e28ed5a876b5e6cd40c12
This comment has been removed by the author.
ReplyDeleteMany thanks for sharing your knowledge
ReplyDeleteApp Builder
Hi JB, no problem :)
DeleteI really like your blog very much and i hope you will continue this good work in the future as well.mobile apps
ReplyDeleteHi Keith, many thanks. I was busy in the last few months, but I will continue with this blog. Great to hear you like it, I will do my best:)
Deletethanks for share...
ReplyDeleteabout time, no thank you to the one week headstart though!
ReplyDeleteMobile Application