1 // ========================================================================
2 // Copyright 2006 Mort Bay Consulting Pty. Ltd.
3 // ------------------------------------------------------------------------
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 // http://www.apache.org/licenses/LICENSE-2.0
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13 // ========================================================================
14
15 package org.mortbay.servlet;
16
17 import java.io.IOException;
18 import java.util.HashMap;
19 import java.util.Map;
20 import java.util.regex.Matcher;
21 import java.util.regex.Pattern;
22
23 import javax.servlet.Filter;
24 import javax.servlet.FilterChain;
25 import javax.servlet.FilterConfig;
26 import javax.servlet.ServletException;
27 import javax.servlet.ServletRequest;
28 import javax.servlet.ServletResponse;
29 import javax.servlet.http.HttpServletRequest;
30
31 /* ------------------------------------------------------------ */
32 /** User Agent Filter.
33 * <p>
34 * This filter allows efficient matching of user agent strings for
35 * downstream or extended filters to use for browser specific logic.
36 * </p>
37 * <p>
38 * The filter is configured with the following init parameters:
39 * <dl>
40 * <dt>attribute</dt><dd>If set, then the request attribute of this name is set with the matched user agent string</dd>
41 * <dt>cacheSize</dt><dd>The size of the user-agent cache, used to avoid reparsing of user agent strings. The entire cache is flushed
42 * when this size is reached</dd>
43 * <dt>userAgent</dt><dd>A regex {@link Pattern} to extract the essential elements of the user agent.
44 * The concatenation of matched pattern groups is used as the user agent name</dd>
45 * <dl>
46 * An example value for pattern is <code>(?:Mozilla[^\(]*\(compatible;\s*+([^;]*);.*)|(?:.*?([^\s]+/[^\s]+).*)</code>. These two
47 * pattern match the common compatibility user-agent strings and extract the real user agent, failing that, the first
48 * element of the agent string is returned.
49 * @author gregw
50 *
51 */
52 public class UserAgentFilter implements Filter
53 {
54 private Pattern _pattern;
55 private Map _agentCache = new HashMap();
56 private int _agentCacheSize=1024;
57 private String _attribute;
58
59 /* ------------------------------------------------------------ */
60 /* (non-Javadoc)
61 * @see javax.servlet.Filter#destroy()
62 */
63 public void destroy()
64 {
65 }
66
67 /* ------------------------------------------------------------ */
68 /* (non-Javadoc)
69 * @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain)
70 */
71 public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
72 {
73 if (_attribute!=null && _pattern!=null)
74 {
75 String ua=getUserAgent(request);
76 request.setAttribute(_attribute,ua);
77 }
78 chain.doFilter(request,response);
79 }
80
81 /* ------------------------------------------------------------ */
82 /* (non-Javadoc)
83 * @see javax.servlet.Filter#init(javax.servlet.FilterConfig)
84 */
85 public void init(FilterConfig filterConfig) throws ServletException
86 {
87 _attribute=filterConfig.getInitParameter("attribute");
88
89 String p=filterConfig.getInitParameter("userAgent");
90 if (p!=null)
91 _pattern=Pattern.compile(p);
92
93 String size=filterConfig.getInitParameter("cacheSize");
94 if (size!=null)
95 _agentCacheSize=Integer.parseInt(size);
96 }
97
98 /* ------------------------------------------------------------ */
99 public String getUserAgent(ServletRequest request)
100 {
101 String ua=((HttpServletRequest)request).getHeader("User-Agent");
102 return getUserAgent(ua);
103 }
104
105 /* ------------------------------------------------------------ */
106 /** Get UserAgent.
107 * The configured agent patterns are used to match against the passed user agent string.
108 * If any patterns match, the concatenation of pattern groups is returned as the user agent
109 * string. Match results are cached.
110 * @param ua A user agent string
111 * @return The matched pattern groups or the original user agent string
112 */
113 public String getUserAgent(String ua)
114 {
115 if (ua==null)
116 return null;
117
118 String tag;
119 synchronized(_agentCache)
120 {
121 tag = (String)_agentCache.get(ua);
122 }
123
124 if (tag==null)
125 {
126 Matcher matcher=_pattern.matcher(ua);
127 if (matcher.matches())
128 {
129 if(matcher.groupCount()>0)
130 {
131 for (int g=1;g<=matcher.groupCount();g++)
132 {
133 String group=matcher.group(g);
134 if (group!=null)
135 tag=tag==null?group:(tag+group);
136 }
137 }
138 else
139 tag=matcher.group();
140 }
141 else
142 tag=ua;
143
144 synchronized(_agentCache)
145 {
146 if (_agentCache.size()>=_agentCacheSize)
147 _agentCache.clear();
148 _agentCache.put(ua,tag);
149 }
150
151 }
152 return tag;
153 }
154 }