View Javadoc

1   package com.atlassian.plugins.rest.common.filter;
2   
3   import com.atlassian.annotations.tenancy.TenantAware;
4   import com.google.common.collect.ImmutableMap;
5   import com.sun.jersey.spi.container.ContainerRequest;
6   import com.sun.jersey.spi.container.ContainerRequestFilter;
7   import org.apache.commons.lang.StringUtils;
8   import org.apache.commons.lang.Validate;
9   
10  import javax.ws.rs.core.HttpHeaders;
11  import javax.ws.rs.core.UriBuilder;
12  import javax.ws.rs.ext.Provider;
13  import java.net.URI;
14  import java.util.Collection;
15  import java.util.LinkedList;
16  import java.util.List;
17  import java.util.Map;
18  import java.util.regex.Pattern;
19  
20  import static com.atlassian.annotations.tenancy.TenancyScope.TENANTLESS;
21  import static javax.ws.rs.core.MediaType.APPLICATION_ATOM_XML;
22  import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
23  import static javax.ws.rs.core.MediaType.APPLICATION_XML;
24  import static javax.ws.rs.core.MediaType.TEXT_HTML;
25  import static javax.ws.rs.core.MediaType.TEXT_PLAIN;
26  
27  /**
28   * <p>A filter to handle URI with extensions. It will set the correct accept header for each extension and remove the extension
29   * from the URI to allow for normal processing of the request.</p>
30   * <p>Currently supported extension and their matching headers are defined in {@link #EXTENSION_TO_ACCEPT_HEADER the extension to header mapping}.</p>
31   * <p>One can exclude URIs from being processed by this filter. Simply create the filter with a collection of {@link Pattern patterns} to be excluded.</p>
32   * <p><strong>Example:</strong> URI <code>http://localhost:8080/app/rest/my/resource.json</code> would be automatically mapped to URI <code>http://localhost:8080/app/rest/my/resource</code>
33   * with its <code>accept</code> header set to <code>application/json</code></p>
34   */
35  @Provider
36  public class ExtensionJerseyFilter implements ContainerRequestFilter {
37      private static final String DOT = ".";
38  
39      private final Collection<Pattern> pathExcludePatterns;
40  
41      public ExtensionJerseyFilter() {
42          pathExcludePatterns = new LinkedList<Pattern>();
43      }
44  
45      public ExtensionJerseyFilter(Collection<String> pathExcludePatterns) {
46          Validate.notNull(pathExcludePatterns);
47          this.pathExcludePatterns = compilePatterns(pathExcludePatterns);
48      }
49  
50      @TenantAware(TENANTLESS)
51      final static Map<String, String> EXTENSION_TO_ACCEPT_HEADER = new ImmutableMap.Builder<String, String>()
52              .put("txt", TEXT_PLAIN)
53              .put("htm", TEXT_HTML)
54              .put("html", TEXT_HTML)
55              .put("json", APPLICATION_JSON)
56              .put("xml", APPLICATION_XML)
57              .put("atom", APPLICATION_ATOM_XML)
58              .build();
59  
60      public ContainerRequest filter(ContainerRequest request) {
61          // the path to the request without query params
62          final String absolutePath = request.getAbsolutePath().toString();
63  
64          final String extension = StringUtils.substringAfterLast(absolutePath, DOT);
65          if (shouldFilter("/" + StringUtils.difference(request.getBaseUri().toString(), absolutePath), extension)) {
66              request.getRequestHeaders().putSingle(HttpHeaders.ACCEPT, EXTENSION_TO_ACCEPT_HEADER.get(extension));
67              final String absolutePathWithoutExtension = StringUtils.substringBeforeLast(absolutePath, DOT);
68              request.setUris(request.getBaseUri(), getRequestUri(absolutePathWithoutExtension, request.getQueryParameters()));
69          }
70          return request;
71      }
72  
73      private boolean shouldFilter(String restPath, String extension) {
74          for (Pattern pattern : pathExcludePatterns) {
75              if (pattern.matcher(restPath).matches()) {
76                  return false;
77              }
78          }
79          return EXTENSION_TO_ACCEPT_HEADER.containsKey(extension);
80      }
81  
82      private URI getRequestUri(String absolutePathWithoutExtension, Map<String, List<String>> queryParams) {
83          final UriBuilder requestUriBuilder = UriBuilder.fromPath(absolutePathWithoutExtension);
84          for (Map.Entry<String, List<String>> queryParamEntry : queryParams.entrySet()) {
85              for (String value : queryParamEntry.getValue()) {
86                  requestUriBuilder.queryParam(queryParamEntry.getKey(), value);
87              }
88          }
89          return requestUriBuilder.build();
90      }
91  
92      private Collection<Pattern> compilePatterns(Collection<String> pathExcludePatterns) {
93          final Collection<Pattern> patterns = new LinkedList<Pattern>();
94          for (String pattern : pathExcludePatterns) {
95              patterns.add(Pattern.compile(pattern));
96          }
97          return patterns;
98      }
99  }