View Javadoc

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 }