Introduction
The ability to detect mobile devices is an important part of website and web application design. Over the last year, requests to sites from mobile devices such as tablets and smartphones have risen to almost 20% of total web traffic. As well as this, dozens of new devices with varying screen sizes, input methods and capabilities are released onto the market all the time. Keeping up with this diversity is a nessecery but difficult task for any organisation maintaining a presence on the web. A good solution is to use a Device Description Repository to detect mobile devices through the browser header and its User Agent. 51Degrees.mobi's professionally maintained device data makes this process easy to implement and stays current through automatic updates.
Dynamically generated web content has the greatest potential to benefit from detailed device detection. We will show how 51Degreesl.mobi's Java detection API can be easily implemented with JavaServer Pages and JavaServer Faces technology, allowing websites to serve users of different devices a dynamically generated experience which can be tailored to the capabilities of their platform. The implementation presented here uses the 51Degrees.mobi Java API available from SourceForge with both the free open source Lite data and the paid for Premium device data.
Prerequisites
In order to make the most from this article, you should have a good knowledge of JSP and JSF. Ideally you should be capable of setting up a simple .jsp page for JSP and an XHTML web page that can call JSF classes, or have access to such a site already. Also, it's a good idea to familiarise yourself with the 51Degrees.mobi Java software. The documentation taking you though downloading, installing and basic usage can be found in our Java Documentation. If you require more information regarding JavaServer Faces, the best place to start would be Oracle's Official Documentation.
Implementation: JavaServer Pages
First thing to do at the top of your main HTML file is to include Java commands to import the necessary library files for the method calls. This uses the JSP Directives tag:
<%@ [content] %>
We will need to include the same four files that the Java documentation lists as necessary:
<%@page import="fiftyone.mobile.detection.BaseDeviceInfo, fiftyone.mobile.detection.Provider" %>
<%@page import="fiftyone.mobile.detection.binary.BinaryException, fiftyone.mobile.detection.binary.Reader" %>
We then need to override the two methods in the JspPage Interface to include the creation and destruction of the “fiftyone.mobile.detection.Provider” Object. This is done using Declaration tags:
<%! [content] %>
<%!
public void jspInit() {
try{
super.Init();
}catch(ServletException ex){
}
try {
getServletContext().setAttribute("51Degrees.mobi", Reader.create());
} catch (BinaryException ex) {
getServletContext().setAttribute("51Degrees.mobi", null);
}
}
public void jspDestroy() {
Provider p = (Provider) getServletContext().getAttribute("51Degrees.mobi");
try {
getServletContext().removeAttribute("51Degrees.mobi");
} finally {
if (p != null) {
p.destroy();
}
}
super.destroy();
}
%>
The bulk of the Java code we will implement must be surrounded in scriptlet tags:
<% [content] %>
You will notice that the code is similar to what we've seen in the Java documentation with the exception of the “request” class. This is used to obtain the HTTP headers including user agent from the request headers sent from the device accessing the site. After we have obtained the headers, we'll need to define a further string to store results.
<%
String userAgent = request.getHeader("User-Agent");
String answer = "UNKNOWN";
%>
Note that the closing scriptlet tag (%>) is not necessary in the final implementation until the end of the code, it is included here for clarity. Of course, using this code as a set of smaller scriptlets is possible.
The next stage is to get the Provider Object, that was created in the jspInit() method, from the servlet context Object. A BaseDeviceInfo Object will also need to be created to hold the result returned from the Provider. This can be achieved using the following lines:
<%
Provider p = (Provider) getServletContext().getAttribute("51Degrees.mobi");
If(p != null){
BaseDeviceInfo b = p.getDeviceInfo(request);
}
%>
The final stage is to put our mobile detect functions to use. The following two print commands can be replaced with flag assignments to help dynamically generate the website returned to the user, or simply to redirect between a desktop site and a mobile site.
<%
if (b!=null && b.getFirstPropertyValue("IsMobile").equalsIgnoreCase("True") == true) {
answer = "Is mobile";
} else {
answer = "Is not mobile";
}
out.println("<h3>The current User-Agent is:</h3> <p>" + userAgent + "</p>");
out.println("<h3>The connecting device is: </h3><p>" + answer + "</p>");
%>
The full JSP code along with the HTML to use it in a real website follows. This should be saved in a .jsp file and the 51degrees.mobi.detection.jar file should be saved in the WEB-INF folder of your web project.
<!-- Import Commands for Java -->
<%@page import="fiftyone.mobile.detection.BaseDeviceInfo, fiftyone.mobile.detection.Provider" %>
<%@page import="fiftyone.mobile.detection.binary.BinaryException, fiftyone.mobile.detection.binary.Reader" %>
<%@page contentType="text/html" pageEncoding="UTF-8" %>
<!-- HTML begins -->
<!DOCTYPE html>
<html>
<!-- Header -->
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>51D Example JSP Page</title>
</head>
<!-- Body -->
<body>
<%!
public void jspInit() {
try{
super.Init();
} catch (ServletException ex){
//Fundamental problem; Handle in the way you deem suitable…
}
try {
getServletContext().setAttribute("51Degrees.mobi", Reader.create());
} catch (BinaryException ex) {
getServletContext().setAttribute("51Degrees.mobi", null);
}
}
public void jspDestroy() {
Provider p = (Provider) getServletContext().getAttribute("51Degrees.mobi");
try {
getServletContext().removeAttribute("51Degrees.mobi");
} finally {
if (p != null) {
p.destroy();
}
}
super.destroy();
}
%>
<!-- Page Title information -->
<h1>51Degrees.mobi Example JSP Page</h1>
<h3>Introduction</h3>
<p>This JSP page is an example of how 51Degrees.mobi can be implemented
using JSP technology.</p>
<!-- Java code begins -->
<%
String userAgent = request.getHeader("User-Agent");
String answer = "UNKNOWN";
Provider p = (Provider) getServletContext().getAttribute("51Degrees.mobi");
//Create the BaseDeviceInfo class and perform a detection on the current user-agent.
BaseDeviceInfo b = p.getDeviceInfo(request);
If (p!=null){
//Take the results of the detection and check whether the device is mobile
if (b!=null)
if (b.getFirstPropertyValue("IsMobile").equalsIgnoreCase("True") == true) {
answer = "Is mobile";
} else {
answer = "Is not mobile";
}
}
}
//Output the detected user-agent and whether the device is mobile or not.
out.println("<h3>The current User-Agent is:</h3> <p>" + userAgent + "</p>");
out.println("<h3>This means that the connecting device: </h3><p>" + answer + "</p>");
%>
<!-- Java code ends -->
</body>
</html>
<!-- HTML Ends -->
Implementation: JavaServer Faces
The JavaServer Faces technology is far more flexible than JSP, but more complex to implement. This guide assumes you already have an application server set up and running. In this case, Glassfish was used, but it will work equally well with Websphere, Tomcat, or any other JSF compliant application server. The first step that needs to be undertaken is to create a Listener Class which implements the ServletContextListener Interface and is noted as such in your “web.xml” file, using the following syntax:
<listener>
<listener-class>
detector.ContextListener51D
</listener-class>
</listener>
A listener class is needed to ensure that a Provider Object is created only once at start-up of your server and that is only destroyed when the server is being shut down and hence no longer needed. On start-up, A Provider is created using the “Reader.create()” method and added to ServletContext attributes. If the Reader class fails to create a provider then an attribute is created that will point to null. On shutdown, the Provider will be retrieved from the attributes and then removed. It's destroy method will then be used to release any resources that it is using so your server will terminate properly:
package detector;
import fiftyone.mobile.detection.Provider;
import fiftyone.mobile.detection.binary.BinaryException;
import fiftyone.mobile.detection.binary.Reader;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
public class ContextListener51D implements ServletContextListener{
@Override
public void contextInitialized(ServletContextEvent sce) {
try{
sce.getServletContext().setAttribute("51Degrees.mobi",Reader.create());
} catch (BinaryException ex){
sce.getServletContext().setAttribute("51Degrees.mobi",null);
}
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
Provider p = (Provider) sce.getServletContext().getAttribute("51Degrees.mobi");
try{
sce.getServletContext().removeAttribute("51Degrees.mobi");
} finally {
p.destroy();
}
}
}
You will then need to create a second class to be able to use the 51Degrees.mobi API to detect mobile incoming devices. This class is very simple and will only require one method to work. This method is detailed below:
public String getIisMobile() {
FacesContext context = FacesContext.getCurrentInstance();
Provider p = (Provider)
context.getExternalContext().getApplicationMap().get("51Degrees.mobi");
if (p != null) {
BaseDeviceInfo b =
p.getDeviceInfo((HttpServletRequest)context.getExternalContext().getRequest());
if (b != null){
if (b.getFirstPropertyValue("IsMobile").equalsIgnoreCase("True")) {
return "Device is mobile";
} else {
return "Device is not mobile";
}
}
}
return "Unknown";
}
The method creates a FacesContext object which allows it to gain access to the incoming request information and retrieve the Provider from the ServletContext's attributes. After it has obtained a result device, using the Provider Object's “getDeviceInfo()” method, it will check the “IsMobile” property of that device and return a string identifying the device as mobile or not. If the Provider was not created successfully or returned null from the “getDeviceInfo()” method then the String “Unknown” will be returned. It should be noted that these are very rare events that may occur if the 51Degrees.mobi data file cannot be read properly or if the User Agent in the request object is corrupted. As a web developer you can then use this identifier to determine which dynamically assigned content should be sent to the user.
The rest of the class structure consists of imports which can be seen below:
package detector;
import fiftyone.mobile.detection.BaseDeviceInfo;
import fiftyone.mobile.detection.Provider;
import java.io.Serializable;
import javax.faces.bean.ApplicationScoped;
import javax.faces.bean.ManagedBean;
import javax.faces.context.FacesContext
import javax.servlet.http.HttpServletRequest;
@ManagedBean
@ApplicationScoped
public class Detector {
//Analyses the detection made in Detector and determines if the device is mobile.
public String getIisMobile() {
FacesContext context = FacesContext.getCurrentInstance();
Provider p =
(Provider) context.getExternalContext().getApplicationMap().get("51Degrees.mobi");
if (p != null) {
BaseDeviceInfo b =
p.getDeviceInfo((HttpServletRequest)context.getExternalContext().getRequest());
if (b != null) {
if (b.getFirstPropertyValue("IsMobile").equalsIgnoreCase("True")) {
return "Is mobile";
} else {
return "Isn't mobile";
}
}
}
return "Unknown";
}
}
Once this file has been implemented, we'll need to call these methods from a web page. For this guide, an XHTML index page has been authored. The XHTML page is displayed here.
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<h:head>
<title>51D Facelet Example</title>
</h:head>
<h:body>
<h:form>
<h1>
51Degrees.mobi Facelet Example
</h1>
<h2>
This Facelet is a basic example of how you can use 51Degrees.mobi
to detect the devices connecting to your site.
</h2>
<h3>The connecting device:</h3>
<h:outputText id="IsMobile" value="#{detector.isMobile}"/>
</h:form>
</h:body>
</html>
The main command in this document which drives the device detection is:
<h:outputText id="IsMobile" value="#{detector.isMobile}"/>
This line calls the “IsMobile” method of the “Detector” class to obtain a detection.
Premium Data
The above guides can be followed with the open source data set available from the 51Degrees.mobi site. The open source Lite solution allows the end user to access values including IsMobile, LayoutEngine, ScreenPixelsWidth/Height, all Ringmark data and many other properties. Use the Property Dictionary on the 51Degrees.mobi website to learn more.
Before using the premium data with the program outlined above, a change must be made to the definition of the Provider “p”:
p = Reader.create("C:/Workspace/JSF/51Degrees.mobi.dat");
Where “C:/Workspace/JSF/51Degrees.mobi.dat” points to your premium device data file.
The premium dataset has a similar implementation, but with a great deal more information about the connecting device. For example, finding out if the connecting device is a smartphone can be done with a similar method. In this method we are passing the BaseDeviceInfo “b”, which we used in the previous example to detect mobile capabilities of the device. If it is mobile, we can pass it to this method which determines whether or not the device is a smartphone. We can then give this device more functionality than a regular mobile device.
public String getIisSmartphone() {
FacesContext context = FacesContext.getCurrentInstance();
Provider p = (Provider)
context.getExternalContext().getApplicationMap().get("51Degrees.mobi");
if (p != null) {
BaseDeviceInfo b =
p.getDeviceInfo((HttpServletRequest)context.getExternalContext().getRequest());
if (b != null) {
if (b.getFirstPropertyValue("IsSmartPhone").equalsIgnoreCase("True")) {
return "Device is a Smart Phone";
} else {
return "Device is not a Smart Phone ";
}
}
return "Unknown";
}
}
This method format can be used for many of the device values. The full list of available values can be found on the property dictionary. Some of the more widely used ones are:
- HasTouchScreen
Displays whether or not the device has a touch screen.
- HasVirtualQwerty and HasQwertyPad
Contains a value that indicates whether the device has either an on screen “virtual” keyboard, or a physical pushbutton one. Useful to know if your website can receive a lot of text input from the user.
- IsTablet
If a device is mobile, this value will tell you if it is a tablet or not.
- ScreenInchesDiagonal
The diagonal size of the screen in inches rather than pixels.
- HardwareModel
The device manufacturers model number. This is more accurate than using the device name.
- CookiesCapable
Will determine the capability of the browser to receive and store cookies.
- BrowserName
Contains the name of the web browser, useful if your website has different formatting specifications for each browser.
“BrowserName” requires a more complex method as this does not return a simple Boolean statement.
public String detectBrowser() {
FacesContext context = FacesContext.getCurrentInstance();
Provider p =
(Provider) context.getExternalContext().getApplicationMap().get("51Degrees.mobi");
BaseDeviceInfo b =
p.getDeviceInfo((HttpServletRequest) context.getExternalContext().getRequest());
return b.getFirstPropertyValue("BrowserName");
}
This will return the name of the browser that is being used. The full list of possible values that this and other properties can contain can be found on the website linked above. The 51degrees.mobi website also has links to our other device detection APIs including PHP, ASP, C and Python as well as full user guides detailing how they can be used with your existing web environment to detect mobile devices, detect mobile operating systems and many other properties.