1   package com.atlassian.plugins.rest.module.servlet;
2   
3   import static com.google.common.collect.Ordering.natural;
4   
5   import com.atlassian.plugin.event.PluginEventManager;
6   import com.atlassian.plugin.servlet.DefaultServletModuleManager;
7   import com.atlassian.plugin.servlet.ServletModuleManager;
8   import com.atlassian.plugin.servlet.descriptors.ServletFilterModuleDescriptor;
9   import com.atlassian.plugin.servlet.descriptors.ServletModuleDescriptor;
10  import com.atlassian.plugin.servlet.filter.FilterLocation;
11  import com.atlassian.plugin.servlet.util.DefaultPathMapper;
12  import com.atlassian.plugin.servlet.util.PathMapper;
13  import com.atlassian.plugins.rest.module.RestApiContext;
14  import com.atlassian.plugins.rest.module.RestServletFilterModuleDescriptor;
15  import com.google.common.collect.Multimaps;
16  import com.google.common.collect.SortedSetMultimap;
17  import com.google.common.collect.TreeMultimap;
18  
19  import org.apache.commons.lang.StringUtils;
20  
21  import javax.servlet.Filter;
22  import javax.servlet.FilterConfig;
23  import javax.servlet.ServletConfig;
24  import javax.servlet.ServletException;
25  import javax.servlet.http.HttpServlet;
26  import java.util.Comparator;
27  import java.util.SortedSet;
28  
29  /**
30   * <p>Servlet module manager to handle REST servlets.</p>
31   */
32  public class DefaultRestServletModuleManager implements RestServletModuleManager
33  {
34      private static final RestServletFilterModuleDescriptorComparator VALUE_COMPARATOR = new RestServletFilterModuleDescriptorComparator();
35  
36      /**
37       * Multimap of filter module descriptors, the key is the "api path" of the REST module descriptor.
38       */
39      private final SortedSetMultimap<String, RestServletFilterModuleDescriptor> filterModuleDescriptors =
40              Multimaps.synchronizedSortedSetMultimap(TreeMultimap.<String, RestServletFilterModuleDescriptor>create(natural(), VALUE_COMPARATOR));
41  
42      private final ServletModuleManager delegateModuleManager;
43      private final PathMapper filterPathMapper;
44      private final String path;
45  
46      public DefaultRestServletModuleManager(PluginEventManager pluginEventManager, String path)
47      {
48          this.filterPathMapper = new DefaultPathMapper();
49          this.delegateModuleManager = new DefaultServletModuleManager(pluginEventManager, new DefaultPathMapper(), filterPathMapper);
50          this.path = StringUtils.isNotBlank(path) ? path : "";
51      }
52  
53      DefaultRestServletModuleManager(ServletModuleManager delegate, PathMapper filterPathMapper, String path)
54      {
55          this.filterPathMapper = filterPathMapper;
56          this.delegateModuleManager = delegate;
57          this.path = StringUtils.isNotBlank(path) ? path : "";
58      }
59  
60      public void addServletModule(ServletModuleDescriptor descriptor)
61      {
62          delegateModuleManager.addServletModule(descriptor);
63      }
64  
65      public HttpServlet getServlet(String path, ServletConfig servletConfig) throws ServletException
66      {
67          return delegateModuleManager.getServlet(path, servletConfig);
68      }
69  
70      public void removeServletModule(ServletModuleDescriptor descriptor)
71      {
72          delegateModuleManager.removeServletModule(descriptor);
73      }
74  
75      public void addFilterModule(ServletFilterModuleDescriptor descriptor)
76      {
77          if (descriptor instanceof RestServletFilterModuleDescriptor)
78          {
79              final RestServletFilterModuleDescriptor restServletFilterModuleDescriptor = (RestServletFilterModuleDescriptor) descriptor;
80              final RestServletFilterModuleDescriptor latest = getRestServletFilterModuleDescriptorForLatest(restServletFilterModuleDescriptor.getBasePath());
81              if (VALUE_COMPARATOR.compare(latest, restServletFilterModuleDescriptor) < 0)
82              {
83                  if (latest != null)
84                  {
85                      filterPathMapper.put(latest.getCompleteKey(), null);
86                      for (String path : latest.getPaths())
87                      {
88                          filterPathMapper.put(latest.getCompleteKey(), path);
89                      }
90                  }
91                  filterPathMapper.put(descriptor.getCompleteKey(), getPathPattern(restServletFilterModuleDescriptor.getBasePath()));
92              }
93              filterModuleDescriptors.put(restServletFilterModuleDescriptor.getBasePath(), restServletFilterModuleDescriptor);
94          }
95          delegateModuleManager.addFilterModule(descriptor);
96      }
97  
98      private RestServletFilterModuleDescriptor getRestServletFilterModuleDescriptorForLatest(String path)
99      {
100         if (path == null)
101         {
102             return null;
103         }
104 
105         final SortedSet<RestServletFilterModuleDescriptor> moduleDescriptors = filterModuleDescriptors.get(path);
106         return moduleDescriptors.isEmpty() ? null : moduleDescriptors.last();
107     }
108 
109     public Iterable<Filter> getFilters(FilterLocation location, String pathInfo, FilterConfig filterConfig) throws ServletException
110     {
111         return delegateModuleManager.getFilters(location, StringUtils.removeStart(pathInfo, path), filterConfig);
112     }
113 
114     public void removeFilterModule(ServletFilterModuleDescriptor descriptor)
115     {
116         if (descriptor instanceof RestServletFilterModuleDescriptor)
117         {
118             final RestServletFilterModuleDescriptor restServletFilterModuleDescriptor = (RestServletFilterModuleDescriptor) descriptor;
119 
120             // check if it was the latest, before removing from the MultiMap
121             RestServletFilterModuleDescriptor latest = getRestServletFilterModuleDescriptorForLatest(restServletFilterModuleDescriptor.getBasePath());
122             filterModuleDescriptors.remove(restServletFilterModuleDescriptor.getBasePath(), restServletFilterModuleDescriptor);
123 
124             if (latest != null && latest.getCompleteKey().equals(descriptor.getCompleteKey()))
125             {
126                 // latest has changed as we have removed an item from the multimap
127                 latest = getRestServletFilterModuleDescriptorForLatest(restServletFilterModuleDescriptor.getBasePath());
128                 if (latest != null)
129                 {
130                     filterPathMapper.put(latest.getCompleteKey(), getPathPattern(latest.getBasePath()));
131                 }
132             }
133         }
134 
135         // remaing mapping of the descriptor will be removed by this call.
136         delegateModuleManager.removeFilterModule(descriptor);
137     }
138 
139     String getPathPattern(String basePath)
140     {
141         return basePath + RestApiContext.LATEST + RestApiContext.ANY_PATH_PATTERN;
142     }
143 
144     private static final class RestServletFilterModuleDescriptorComparator implements Comparator<RestServletFilterModuleDescriptor>
145     {
146         public int compare(RestServletFilterModuleDescriptor descriptor1, RestServletFilterModuleDescriptor descriptor2)
147         {
148             if (descriptor1 == null)
149             {
150                 return -1;
151             }
152             if (descriptor2 == null)
153             {
154                 return +1;
155             }
156             return descriptor1.getVersion().compareTo(descriptor2.getVersion());
157         }
158     }
159 }