org.milyn.servlet.ServletUAContext Maven / Gradle / Ivy
The newest version!
/*
Milyn - Copyright (C) 2006 - 2010
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License (version 2.1) as published by the Free Software
Foundation.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU Lesser General Public License for more details:
http://www.gnu.org/licenses/lgpl.txt
*/
package org.milyn.servlet;
import org.milyn.useragent.UnknownUseragentException;
import org.milyn.profile.Profile;
import org.milyn.profile.ProfileSet;
import org.milyn.useragent.UAContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import javax.servlet.ServletConfig;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Vector;
/**
* Servlet useragent context.
*
* Requirements
*
* - JDK 1.4+
* - Servlet Specification 2.3+ compliant container
*
*
* Deployment
* Deploying Tinak in a J2EE Servlet Container is very simple:
*
* - Download and explode the Tinak distribution.
* - Deploy the Tinak binaries into the Servlet container i.e. into
/WEB-INF/lib
.
* The binaries are located in the "build" folder in the distribution.
* - Deploy the Tinak dependencies into the Servlet container i.e. into
/WEB-INF/lib
.
* The dependencies are located in the "lib" folder in the distribution.
* Note: Don't copy the servlet.jar file to the target container.
*
* - Deploy the Tinak device recognition XML module in the container. It's default
* deployment location is
/WEB-INF/device-ident.xml
but this can be configured
* in the deployment descriptor. A sample device-ident.xml can be found in the root
* of the distribution
* - Deploy the Tinak device profiling XML module in the container. It's default
* deployment location is
/WEB-INF/device-profile.xml
but this can be configured
* in the deployment descriptor. A sample device-profile.xml can be found in the root
* of the distribution
*
*
* As stated at the start of this section, the Tinak device recognition XML module
* default deployment location can be overridden. This is done through the web
* application's deployment descriptor as follows:
*
* <servlet>
* <servlet-name>aservlet</servlet-name>
* <servlet-class>org.milyn.some.AServlet</servlet-class>
* <init-param>
* <param-name>DeviceIdentUrl</param-name>
* <param-value>URL</param-value>
* </init-param>
* </servlet>
*
* or,
*
* <context-param>
* <param-name>DeviceIdentUrl</param-name>
* <param-value>URL</param-value>
* </context-param>
*
* checked by Tinak in that order. URL can be a context relative URL or a URL to an external resource
* i.e. and absolute URL. The ability to define an external URL means that two or more web
* applications can share the same configuration. It also means that the device recognition
* data can be stored in a database (or some other format) and accessed as an XML stream via a HTTP request.
* See "Device Recognition" for more.
*
* The default deployment location of the profiling XML module can also be overridden in exactly the same fashion.
* The name of the parameter in this case is "DeviceProfileUrl".
*
* Adding Browser Recognition & Profiling to a J2EE Servlet
* Adding browser recognition and profiling support to a Servlet implementation
* using Tinak is very simple - literally a one-liner.
*
* It's done using this class as follows:
*
* try {
* {@link UAContext} uaContext = ServletUAContext.{@link ServletUAContext#getInstance(HttpServletRequest, ServletConfig)};
* } catch({@link org.milyn.device.ident.UnknownDeviceException} unknownDevice) {
* // Handle Exception...
* }
* The {@link UAContext} instance can then be used to:
*
* - get the browser (device) common name, and
* - check the browser for membership of a given profile.
*
* See the online user docs for details on how to configure
* device recognition and profiling in a Servlet container.
* @author Tom Fennelly
*/
public final class ServletUAContext implements UAContext {
/**
* The useragent common name.
*/
private String commonName = null;
/**
* The ProfileSet. Set of profiles of which the device has membership.
*/
private ProfileSet profileSet = null;
/**
* ServletUAContext session key.
*/
protected static final String CONTEXT_KEY = "xxx." + ServletUAContext.class.getName() + ".Context_KEY";
/**
* Table of preconstructed ServletUAContext instances keyed by their common name.
*/
private static Hashtable contexts = new Hashtable();
/**
* Profiles Servlet Context key.
*/
public static final String PROFILES_KEY = "xxx." + ServletUAContext.class.getName() + ".ctxProfiles";
/**
* Private constructor.
*
* Hiding the constructor.
* @param commonName The device common name.
*/
private ServletUAContext(String commonName) {
if(commonName == null) {
throw new IllegalArgumentException("null commonName arg in constructor call.");
}
this.commonName = commonName;
}
/**
* Get the Useragent Context for the Servlet request.
*
* Factory construction method.
* @param request The HttpServletRequest instance associated with the request.
* @param config The Servet Configuration.
* @return Useragent context.
* @throws UnknownUseragentException Device match failure.
*/
public static UAContext getInstance(HttpServletRequest request, ServletConfig config) throws UnknownUseragentException {
ServletUAContext context;
final String deviceMatchCName;
HttpSession session;
if(request == null) {
throw new IllegalArgumentException("null 'request' arg in call to getInstance()");
} else if(config == null) {
throw new IllegalArgumentException("null 'config' arg in call to getInstance()");
}
// Is the context already set in the request
context = (ServletUAContext)request.getAttribute(CONTEXT_KEY);
if(context != null) {
return context;
}
// Is the context set in the session
session = request.getSession();
context = (ServletUAContext)session.getAttribute(CONTEXT_KEY);
if(context != null) {
request.setAttribute(CONTEXT_KEY, context);
return context;
}
// OK, need to match the device and store the context.
deviceMatchCName = DeviceIdentifier.matchDevice(config, request);
context = (ServletUAContext)contexts.get(deviceMatchCName);
if(context == null) {
context = new ServletUAContext(deviceMatchCName);
context.profileSet = DeviceProfiler.getDeviceProfile(deviceMatchCName, request, config);
if(context.profileSet == null) {
context.profileSet = new ProfileSet() {
public String getBaseProfile() {
return deviceMatchCName;
}
public boolean isMember(String profile) {
return false;
}
public void addProfile(Profile profile) {
}
public Profile getProfile(String profile) {
return null;
}
public Iterator iterator() {
// return an empty iterator
return (new Vector()).iterator();
}};
}
contexts.put(deviceMatchCName, context);
}
request.setAttribute(CONTEXT_KEY, context);
session.setAttribute(CONTEXT_KEY, context);
return context;
}
/**
* Get the useragent common name.
* @return The useragent common name.
*/
public String getCommonName() {
return commonName;
}
/**
* Set the useragent common name.
* @param name The useragent common name.
*/
private void setDeviceName(String name) {
commonName = name;
}
/**
* Get the ProfileSet for the device.
* @return The ProfileSet
*/
public ProfileSet getProfileSet() {
if(profileSet == null) {
throw new IllegalStateException("Call to 'isMember' before device has been matched.");
}
return profileSet;
}
/**
* Unit Test class.
*/
static class UnitTest {
static UAContext getUAContext(String commonName) {
return new ServletUAContext(commonName);
}
}
}