View Javadoc

1   package com.atlassian.plugins.rest.module.filter;
2   
3   import com.sun.jersey.spi.container.ContainerRequest;
4   import com.sun.jersey.spi.container.ContainerRequestFilter;
5   import org.apache.commons.lang.StringUtils;
6   
7   import javax.ws.rs.core.MediaType;
8   import javax.ws.rs.core.MultivaluedMap;
9   import javax.ws.rs.ext.Provider;
10  import java.util.HashSet;
11  import java.util.Set;
12  
13  import static javax.ws.rs.core.HttpHeaders.ACCEPT;
14  import static javax.ws.rs.core.MediaType.APPLICATION_ATOM_XML;
15  import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
16  import static javax.ws.rs.core.MediaType.APPLICATION_XML;
17  import static javax.ws.rs.core.MediaType.TEXT_HTML;
18  import static javax.ws.rs.core.MediaType.TEXT_PLAIN;
19  import static javax.ws.rs.core.MediaType.WILDCARD;
20  
21  /**
22   * <p>This is a filter to "fix" user agents with broken {@code Accept} headers.</p> <p>When a client accepts {@code
23   * text/html} we want it to be first in the header. This is because, it is likely that it is a browser and we then
24   * believe that HTML is the best view to render.</p> <p>This was introduced specifically to make Jersey MVC work with
25   * webkit based browsers which by default have their accept header set to {@code application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*\/*;q=0.5}</p>
26   * <p>The "fix" is to prepend {@code text/html} content type in the {@code Accept} header if it exists anywhere (else)
27   * in the header.</p>
28   * <p/>
29   * <p>When a client accepts {@link MediaType#WILDCARD} (IE) we want to prepend it with {@code text/html}.
30   */
31  @Provider
32  public class AcceptHeaderJerseyMvcFilter implements ContainerRequestFilter {
33      static final Set<String> ACCEPTED_CONTENT_TYPES = new HashSet<String>() {{
34          add(TEXT_PLAIN);
35          add(TEXT_HTML);
36          add(TEXT_HTML);
37          add(APPLICATION_JSON);
38          add(APPLICATION_XML);
39          add(APPLICATION_ATOM_XML);
40      }};
41  
42  
43      public ContainerRequest filter(final ContainerRequest request) {
44          final MultivaluedMap<String, String> requestHeaders = request.getRequestHeaders();
45          final String acceptHeader = requestHeaders.getFirst(ACCEPT); // there should only be one Accept header
46  
47          String fixedHeader = addAppXmlWhenWildcardOnly(acceptHeader);
48          fixedHeader = moveTextHtmlToFront(fixedHeader);
49  
50          if (acceptHeader != null && !acceptHeader.equals(fixedHeader)) {
51              requestHeaders.putSingle(ACCEPT, fixedHeader);
52          }
53  
54          return request;
55      }
56  
57      private String addAppXmlWhenWildcardOnly(final String acceptHeader) {
58          if (StringUtils.contains(acceptHeader, WILDCARD)) {
59              for (String contentType : ACCEPTED_CONTENT_TYPES) {
60                  if (StringUtils.contains(acceptHeader, contentType)) {
61                      return acceptHeader;
62                  }
63              }
64              return APPLICATION_XML + "," + acceptHeader;
65          }
66          return acceptHeader;
67      }
68  
69      private String moveTextHtmlToFront(final String acceptHeader) {
70          if ((StringUtils.contains(acceptHeader, TEXT_HTML) || StringUtils.contains(acceptHeader, WILDCARD))
71                  && !StringUtils.startsWith(acceptHeader, TEXT_HTML)) {
72              return TEXT_HTML + "," + acceptHeader;
73          }
74          return acceptHeader;
75      }
76  }