001package org.apache.turbine.util;
002
003/*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements.  See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership.  The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License.  You may obtain a copy of the License at
011 *
012 *   http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied.  See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022import java.util.concurrent.ConcurrentHashMap;
023import java.util.concurrent.ConcurrentMap;
024
025import nl.basjes.parse.useragent.UserAgent;
026import nl.basjes.parse.useragent.UserAgentAnalyzer;
027
028/**
029 * This class parses the user agent string and provides getters for
030 * its parts. It uses YAUAA (https://yauaa.basjes.nl/)
031 *
032 * The initialization step for a full UserAgentAnalyzer
033 * (i.e. all fields) usually takes something in the range of 2-5 seconds.
034 *
035 * @author <a href="mailto:frank.kim@clearink.com">Frank Y. Kim</a>
036 * @author <a href="mailto:leon@clearink.com">Leon Atkisnon</a>
037 * @author <a href="mailto:mospaw@polk-county.com">Chris Mospaw</a>
038 * @author <a href="mailto:bgriffin@cddb.com">Benjamin Elijah Griffin</a>
039 * @author <a href="mailto:tv@apache.org">Thomas Vandahl</a>
040 */
041public class BrowserDetector
042{
043    /** The user agent string. */
044    private String userAgentString = "";
045
046    /** The user agent parser */
047    private static UserAgentAnalyzer uaa = UserAgentAnalyzer
048            .newBuilder()
049            .withFields(UserAgent.AGENT_NAME,
050                    UserAgent.AGENT_VERSION,
051                    UserAgent.OPERATING_SYSTEM_NAME)
052            .hideMatcherLoadStats()
053            .build();
054
055    /** The user agent cache. */
056    private static volatile ConcurrentMap<String, UserAgent> userAgentCache =
057            new ConcurrentHashMap<>();
058
059    /** The browser name specified in the user agent string. */
060    private String browserName = "";
061
062    /**
063     * The browser version specified in the user agent string.  If we
064     * can't parse the version just assume an old browser.
065     */
066    private float browserVersion = (float) 1.0;
067
068    /**
069     * The browser platform specified in the user agent string.
070     */
071    private String browserPlatform = "unknown";
072
073    /**
074     * Constructor used to initialize this class.
075     *
076     * @param userAgentString A String with the user agent field.
077     */
078    public BrowserDetector(String userAgentString)
079    {
080        this.userAgentString = userAgentString;
081        UserAgent userAgent = getUserAgent();
082
083        // Get the browser name and version.
084        browserName = userAgent.getValue(UserAgent.AGENT_NAME);
085        String version = userAgent.getValue(UserAgent.AGENT_VERSION);
086        browserVersion = toFloat(version);
087
088        // Try to figure out what platform.
089        browserPlatform = userAgent.getValue(UserAgent.OPERATING_SYSTEM_NAME);
090    }
091
092    /**
093     * Constructor used to initialize this class.
094     *
095     * @param data The Turbine RunData object.
096     */
097    public BrowserDetector(RunData data)
098    {
099        this(data.getUserAgent());
100    }
101
102    /**
103     * The browser name specified in the user agent string.
104     *
105     * @return A String with the browser name.
106     */
107    public String getBrowserName()
108    {
109        return browserName;
110    }
111
112    /**
113     * The browser platform specified in the user agent string.
114     *
115     * @return A String with the browser platform.
116     */
117    public String getBrowserPlatform()
118    {
119        return browserPlatform;
120    }
121
122    /**
123     * The browser version specified in the user agent string.
124     *
125     * @return A String with the browser version.
126     */
127    public float getBrowserVersion()
128    {
129        return browserVersion;
130    }
131
132    /**
133     * The user agent string for this class.
134     *
135     * @return A String with the user agent.
136     */
137    public String getUserAgentString()
138    {
139        return userAgentString;
140    }
141
142    /**
143     * The user agent for this class.
144     *
145     * @return A user agent.
146     */
147    public UserAgent getUserAgent()
148    {
149        return parse(userAgentString);
150    }
151
152    /**
153     * Helper method to initialize this class.
154     *
155     * @param userAgentString the user agent string
156     */
157    private static UserAgent parse(String userAgentString)
158    {
159        return userAgentCache.computeIfAbsent(userAgentString, uaa::parse);
160    }
161
162    /**
163     * Helper method to convert String to a float.
164     *
165     * @param s A String.
166     * @return The String converted to float.
167     */
168    private static final float toFloat(String s)
169    {
170        return Float.parseFloat(s);
171    }
172}