View Javadoc

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