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.ArrayList;
19 import java.util.Enumeration;
20 import java.util.HashMap;
21 import java.util.List;
22 import java.util.Map;
23 import java.util.regex.Matcher;
24 import java.util.regex.Pattern;
25
26 import javax.servlet.Filter;
27 import javax.servlet.FilterChain;
28 import javax.servlet.FilterConfig;
29 import javax.servlet.ServletException;
30 import javax.servlet.ServletRequest;
31 import javax.servlet.ServletResponse;
32 import javax.servlet.http.HttpServletRequest;
33
34 /* ------------------------------------------------------------ */
35 /** User Agent Filter.
36 * <p>
37 * This filter allows efficient matching of user agent strings for
38 * downstream or extended filters to use for browser specific logic.
39 * </p>
40 * <p>
41 * The filter is configured with the following init parameters:
42 * <dl>
43 * <dt>attribute</dt><dd>If set, then the request attribute of this name is set with the matched user agent string</dd>
44 * <dt>cacheSize</dt><dd>The size of the user-agent cache, used to avoid reparsing of user agent strings. The entire cache is flushed
45 * when this size is reached</dd>
46 * <dt>userAgent</dt><dd>A regex {@link Pattern} to extract the essential elements of the user agent.
47 * The concatenation of matched pattern groups is used as the user agent name</dd>
48 * <dl>
49 * An example value for pattern is <code>(?:Mozilla[^\(]*\(compatible;\s*+([^;]*);.*)|(?:.*?([^\s]+/[^\s]+).*)</code>. These two
50 * pattern match the common compatibility user-agent strings and extract the real user agent, failing that, the first
51 * element of the agent string is returned.
52 * @author gregw
53 *
54 */
55 public class UserAgentFilter implements Filter
56 {
57 private Pattern _pattern;
58 private Map _agentCache = new HashMap();
59 private int _agentCacheSize=1024;
60 private String _attribute;
61
62 /* ------------------------------------------------------------ */
63 /* (non-Javadoc)
64 * @see javax.servlet.Filter#destroy()
65 */
66 public void destroy()
67 {
68 }
69
70 /* ------------------------------------------------------------ */
71 /* (non-Javadoc)
72 * @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain)
73 */
74 public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
75 {
76 if (_attribute!=null && _pattern!=null)
77 {
78 String ua=getUserAgent(request);
79 request.setAttribute(_attribute,ua);
80 }
81 chain.doFilter(request,response);
82 }
83
84 /* ------------------------------------------------------------ */
85 /* (non-Javadoc)
86 * @see javax.servlet.Filter#init(javax.servlet.FilterConfig)
87 */
88 public void init(FilterConfig filterConfig) throws ServletException
89 {
90 _attribute=filterConfig.getInitParameter("attribute");
91
92 String p=filterConfig.getInitParameter("userAgent");
93 if (p!=null)
94 _pattern=Pattern.compile(p);
95
96 String size=filterConfig.getInitParameter("cacheSize");
97 if (size!=null)
98 _agentCacheSize=Integer.parseInt(size);
99 }
100
101 /* ------------------------------------------------------------ */
102 public String getUserAgent(ServletRequest request)
103 {
104 String ua=((HttpServletRequest)request).getHeader("User-Agent");
105 return getUserAgent(ua);
106 }
107
108 /* ------------------------------------------------------------ */
109 /** Get UserAgent.
110 * The configured agent patterns are used to match against the passed user agent string.
111 * If any patterns match, the concatenation of pattern groups is returned as the user agent
112 * string. Match results are cached.
113 * @param ua A user agent string
114 * @return The matched pattern groups or the original user agent string
115 */
116 public String getUserAgent(String ua)
117 {
118 if (ua==null)
119 return null;
120
121 String tag;
122 synchronized(_agentCache)
123 {
124 tag = (String)_agentCache.get(ua);
125 }
126
127 if (tag==null)
128 {
129 Matcher matcher=_pattern.matcher(ua);
130 if (matcher.matches())
131 {
132 if(matcher.groupCount()>0)
133 {
134 for (int g=1;g<=matcher.groupCount();g++)
135 {
136 String group=matcher.group(g);
137 if (group!=null)
138 tag=tag==null?group:(tag+group);
139 }
140 }
141 else
142 tag=matcher.group();
143 }
144 else
145 tag=ua;
146
147 synchronized(_agentCache)
148 {
149 if (_agentCache.size()>=_agentCacheSize)
150 _agentCache.clear();
151 _agentCache.put(ua,tag);
152 }
153
154 }
155 return tag;
156 }
157 }