BrowserDetector.java
package sk.iway.iwcm.stat;
import sk.iway.iwcm.*;
import sk.iway.iwcm.components.seo.SeoManager;
import sk.iway.iwcm.io.IwcmFile;
import sk.iway.iwcm.io.IwcmInputStream;
import javax.servlet.http.HttpServletRequest;
import org.apache.struts.util.ResponseUtils;
import java.io.Serializable;
import java.util.StringTokenizer;
import ua_parser.Parser;
import ua_parser.Client;
/**
* Parse User-Agent string to detect browser name, version and platform.
*/
public class BrowserDetector implements Serializable {
/**
* Comment for <code>serialVersionUID</code>
*/
private static final long serialVersionUID = -4467029894704819932L;
/**
* The user agent string.
*/
private String userAgentString = "";
/**
* The browser name specified in the user agent string.
*/
private String browserName = "";
/**
* The browser version specified in the user agent string. If we can't parse
* the version just assume an old browser.
*/
private String browserVersion = "0.0";
/**
* The browser platform specified in the user agent string.
*/
private String browserPlatform = "unknown";
private String browserSubplatform = "";
private boolean phone = false;
private boolean tablet = false;
private boolean amp;
private String browserDeviceType = null;
public static final String MSIE = "MSIE";
public static final String OPERA = "Opera";
public static final String MOZILLA = "Mozilla";
public static final String FIREFOX = "Firefox";
public static final String SAFARI = "Safari";
public static final String CHROME = "Chrome";
public static final String WINDOWS = "Windows";
public static final String UNIX = "Unix";
public static final String MACINTOSH = "macOS";
// boty maskujuce sa ako Mozilla
protected static final String[] BOTS = { "Googlebot", "Yahoo!", "Charlotte", "Wauuu", "VoilaBot" };
private int browserUaId = 0;
private int platformId = 0;
private int subplatformId = 0;
private String country = null;
private static Parser uaParser = null;
static {
String configPath = Constants.getString("uaParserYamlPath");
if (Tools.isNotEmpty(configPath)) {
try {
IwcmFile yamlFile = new IwcmFile(Tools.getRealPath(configPath));
if (yamlFile.exists() && yamlFile.isFile() && yamlFile.canRead()) {
Logger.debug(BrowserDetector.class, "loading parser from " + yamlFile.getAbsolutePath());
IwcmInputStream is = new IwcmInputStream(yamlFile);
uaParser = new Parser(is, Parser.getDefaultLoaderOptions());
is.close();
}
} catch (Exception ie) {
Logger.error(BrowserDetector.class, "Failed to initialize parser from "+configPath, ie);
}
}
if (uaParser == null) uaParser = new Parser();
}
/**
* Ziska instanciu detektora prehliadaca a ulozi do session na neskorsie
* pouzitie
*
* @param request
* @return
*/
public static BrowserDetector getInstance(HttpServletRequest request) {
if (request.getParameter("forceBrowserDetector") != null)
request.getSession().removeAttribute(StatDB.BROWSER_DETECTOR);
BrowserDetector browser = (BrowserDetector) request.getSession().getAttribute(StatDB.BROWSER_DETECTOR);
if (browser != null) {
return browser;
}
// double check cez Cache objekt (kvoli botom)
String KEY = null;
Cache c = null;
if (request.getParameter("forceBrowserDetector") == null) {
c = Cache.getInstance();
KEY = "browserDetector-" + Tools.getRemoteIP(request) + "-" + request.getHeader("User-Agent");
browser = (BrowserDetector) c.getObject(KEY);
if (browser != null) {
return browser;
}
}
browser = new BrowserDetector(request);
request.getSession().setAttribute(StatDB.BROWSER_DETECTOR, browser);
if (c != null) {
c.setObjectSeconds(KEY, browser, 5 * 60);
}
return browser;
}
public static boolean isStatIpAllowedFast(HttpServletRequest request) {
if (request == null)
return true;
BrowserDetector browser = (BrowserDetector) request.getSession().getAttribute(StatDB.BROWSER_DETECTOR);
if (browser != null) {
return browser.isStatIpAllowed();
}
return isStatIpAllowedImpl(request);
}
private static boolean isStatIpAllowedImpl(HttpServletRequest request) {
try {
String statNoLogIP = Constants.getString("statNoLogIP");
if (statNoLogIP != null) {
StringTokenizer st = new StringTokenizer(statNoLogIP, ",");
String ip;
String myIP = Tools.getRemoteIP(request);
while (st.hasMoreTokens()) {
ip = st.nextToken();
if (myIP.startsWith(ip)) {
return false;
}
}
}
} catch (Exception ex) {
sk.iway.iwcm.Logger.error(ex);
}
return true;
}
public BrowserDetector(String userAgentString) {
this.userAgentString = userAgentString;
parse(null);
}
/**
* Konstruktor objektu, nemal by sa pouzivat, treba pouzivat
* getInstance(request)
*
* @param request
*/
public BrowserDetector(HttpServletRequest request) {
this.userAgentString = request.getHeader("User-Agent");
Logger.debug(BrowserDetector.class, "BD: get Instance, ip=" + Tools.getRemoteIP(request) + " ua=" + userAgentString);
parse(request);
statIpAllowed = isStatIpAllowedImpl(request);
statUserAgentAllowed = true;
String statDisableUserAgent = Constants.getString("statDisableUserAgent");
if (Tools.isNotEmpty(statDisableUserAgent)) {
StringTokenizer st = new StringTokenizer(statDisableUserAgent.toLowerCase(), ",;+");
// potrebujeme full UA
String userAgent = request.getHeader("User-Agent");
if (Tools.isNotEmpty(userAgent)) {
if (userAgent.indexOf('(') == -1) {
// je velmi nepravdepodobne, ze normalny UA tam nema nieco v ()
statUserAgentAllowed = false;
} else {
userAgent = userAgent.toLowerCase();
while (st.hasMoreTokens()) {
String token = st.nextToken().trim();
if (userAgent.indexOf(token) != -1) {
statUserAgentAllowed = false;
break;
}
}
}
} else {
// ak je UA prazdne, povazujeme to za robota
statUserAgentAllowed = false;
}
/**
* ulozi vyhladavaci stroj do tabulky kvoli statistike
*/
if (!statUserAgentAllowed) {
SeoManager.addSearchEngineVisit(this.browserName + " " + this.browserVersion);
if ("unknown".equals(getBrowserPlatform()))
browserPlatform = "Search Bot";
}
}
// nastav idecka
browserUaId = StatDB.getStatKeyId(getBrowserName() + " " + getBrowserVersionShort());
platformId = StatDB.getStatKeyId(getBrowserPlatform());
subplatformId = StatDB.getStatKeyId(getBrowserSubplatform());
}
/**
* Helper method to initialize this class.
*/
public void parse(HttpServletRequest request) {
try {
if (userAgentString == null)
return;
Client uaClient = uaParser.parse(userAgentString);
browserName = uaClient.userAgent.family;
if (uaClient.userAgent.major!=null) browserVersion = uaClient.userAgent.major+"."+uaClient.userAgent.minor;
browserPlatform = uaClient.os.family;
browserSubplatform = uaClient.os.major;
String forceBrowserDetector = forceBrowserTypeIfRequested(request);
detectBrowserDeviceType();
// nastav globalnu premennu
if (Tools.isNotEmpty(forceBrowserDetector))
browserDeviceType = forceBrowserDetector;
if ("phone".equals(forceBrowserDetector)) {
phone = true;
} else if ("amp".equals(forceBrowserDetector)) {
amp = true;
} else if ("tablet".equals(forceBrowserDetector)) {
tablet = true;
} else if ("smartphoneOrTablet".equals(forceBrowserDetector)) {
tablet = true;
} else if ("normal".equals(forceBrowserDetector) || "pc".equals(forceBrowserDetector)) {
phone = false;
tablet = false;
browserDeviceType = null;
}
detectCountry(request);
} catch (Exception e) {
Logger.error(BrowserDetector.class, "BD PARSE: ua=" + userAgentString);
sk.iway.iwcm.Logger.error(e);
}
}
private String forceBrowserTypeIfRequested(HttpServletRequest request) {
if (request == null) return null;
String forceBrowserDetector = null;
if (request.getSession()!=null) {
forceBrowserDetector = (String) request.getSession().getAttribute("BrowserDetector.forceBrowserDetector");
}
if (Tools.isNotEmpty(request.getParameter("forceBrowserDetector"))) {
forceBrowserDetector = request.getParameter("forceBrowserDetector").toLowerCase();
if ("pc".equals(forceBrowserDetector)) {
forceBrowserDetector = null;
request.getSession().removeAttribute("BrowserDetector.forceBrowserDetector");
} else {
request.getSession().setAttribute("BrowserDetector.forceBrowserDetector", forceBrowserDetector);
}
}
return forceBrowserDetector;
}
private void detectBrowserDeviceType() {
// null lebo s nim narabame tak v Template device type (historicka zalezitost)
browserDeviceType = null;
String ua = getUserAgentString().toLowerCase();
if (ua.contains("ipad") || ua.contains("tablet") || ua.contains("kindle")) {
tablet = true;
browserDeviceType = "tablet";
} else if (ua.contains("iphone")) {
phone = true;
browserDeviceType = "phone";
} else if (ua.contains("android")) {
// https://stackoverflow.com/questions/5341637/how-do-detect-android-tablets-in-general-useragent
if (ua.contains("mobile")) {
phone = true;
browserDeviceType = "phone";
} else {
tablet = true;
browserDeviceType = "tablet";
}
}
}
private void detectCountry(HttpServletRequest request) {
if (request == null) {
country = "unknown";
return;
}
country = Tools.getRemoteHost(request);
if (country == null || country.length() < 2) {
country = "unknown";
}
int index = country.lastIndexOf('.');
if (index > 0) {
country = country.substring(index + 1);
} else {
country = "unknown";
}
// ak host je cislo je to IP adresa, nemame meno...
try {
Integer.parseInt(country);
country = "unknown";
} catch (Exception ex) {
}
if ("unknown".equals(country)) {
// skusme vydedukovat podla accept language
try {
// sk,en-us;q=0.7,en;q=0.3
String acceptLanguage = request.getHeader("accept-language");
if (Tools.isNotEmpty(acceptLanguage)) {
String[] alArray = acceptLanguage.split(",");
if (alArray.length > 0) {
String lang = alArray[0];
try {
// en-us;q=0.7
if (lang.indexOf(';') != -1)
lang = lang.split(";")[0];
// en-us
if (lang.indexOf('-') != -1)
lang = lang.split("-")[1];
// sk_SK
if (lang.indexOf('_') != -1)
lang = lang.split("_")[1];
} catch (Exception e) {
}
lang = lang.toLowerCase();
String domain = StatDB.getLanguageDomainTable().get(lang);
if (domain != null)
lang = domain;
country = lang;
}
} else {
Logger.debug(StatDB.class, "Unknown IP & lang: ua=" + request.getHeader("User-Agent"));
}
} catch (Exception e) {
Logger.error(StatDB.class, "Unknown IP & lang: ua=" + request.getHeader("User-Agent")
+ " acceptLanguage=" + request.getHeader("accept-language"));
sk.iway.iwcm.Logger.error(e);
}
}
if ("gb".equals(country))
country = "co.uk";
if (country.indexOf('_') > 0) //NOSONAR
country = country.substring(0, country.indexOf('_'));
if (country.indexOf('-') > 0) //NOSONAR
country = country.substring(0, country.indexOf('-'));
country = DB.internationalToEnglish(DB.prepareString(country, 4).toLowerCase());
}
/**
* Ak je nastavene true zaznamenava sa statistika pre aktualnu IP adresu
*/
private boolean statIpAllowed = true;
public boolean isStatIpAllowed() {
return statIpAllowed;
}
/**
* Ak je nastavene na true zaznamenava sa statistika pre aktualneho user agenta
*/
private boolean statUserAgentAllowed = true;
public boolean isStatUserAgentAllowed() {
return statUserAgentAllowed;
}
public String getBrowserName() {
if (Tools.isEmpty(browserName) || "Other".equals(browserName)) return "Unknown";
if ("IE".equals(browserName)) return "MSIE";
return ResponseUtils.filter(browserName);
}
public String getBrowserPlatform() {
if (Tools.isEmpty(browserPlatform) || "Other".equals(browserPlatform)) return "unknown";
return ResponseUtils.filter(browserPlatform);
}
public String getBrowserVersion() {
return browserVersion;
}
public String getBrowserVersionShort() {
if (browserVersion == null) return "";
int index = browserVersion.indexOf('.');
if (index != -1) {
return browserVersion.substring(0, index);
}
return browserVersion;
}
public String getUserAgentString() {
return ResponseUtils.filter(userAgentString);
}
public String getBrowserSubplatform() {
return ResponseUtils.filter(browserSubplatform);
}
public boolean isAmp() {
return amp;
}
public boolean isTablet() {
return tablet;
}
public boolean isPhone() {
return phone;
}
public boolean isDesktop() {
if (isTablet() || isPhone())
return false;
return true;
}
/**
* Vrati true ak sa jedna o iPhone, iPad alebo Android zariadenie
*
* @return
*/
public boolean isSmartphoneOrTablet() {
return tablet || phone;
}
/**
* Vrati true ak sa jedna o iPhone, iPad alebo Android zariadenie
*
* @param request
* @return
*/
public static boolean isSmartphoneOrTablet(HttpServletRequest request) {
BrowserDetector bd = BrowserDetector.getInstance(request);
return bd.isSmartphoneOrTablet();
}
public String getBrowserDeviceType() {
return ResponseUtils.filter(browserDeviceType);
}
public int getBrowserUaId() {
return browserUaId;
}
public int getPlatformId() {
return platformId;
}
public int getSubplatformId() {
return subplatformId;
}
/**
* Max 4 pismenkovy kod krajiny
*
* @return
*/
public String getCountry() {
return ResponseUtils.filter(country);
}
}