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.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 }