View Javadoc

1   package com.atlassian.plugin.webresource;
2   
3   import com.atlassian.plugin.PluginAccessor;
4   import com.atlassian.plugin.ModuleDescriptor;
5   import com.atlassian.plugin.Plugin;
6   import com.atlassian.plugin.elements.ResourceDescriptor;
7   import com.atlassian.plugin.elements.ResourceLocation;
8   import org.apache.commons.logging.Log;
9   import org.apache.commons.logging.LogFactory;
10  import org.apache.commons.lang.StringUtils;
11  
12  import java.util.*;
13  
14  import com.atlassian.plugin.servlet.ServletContextFactory;
15  import com.atlassian.plugin.servlet.DownloadableResource;
16  import com.atlassian.plugin.servlet.DownloadableWebResource;
17  import com.atlassian.plugin.servlet.DownloadableClasspathResource;
18  import com.atlassian.plugin.servlet.ForwardableResource;
19  
20  /**
21   * Default implementation of {@link PluginResourceLocator}.
22   * @since 2.2
23   */
24  public class PluginResourceLocatorImpl implements PluginResourceLocator
25  {
26      private static final Log log = LogFactory.getLog(PluginResourceLocatorImpl.class);
27  
28      public static final String PLUGIN_WEBRESOURCE_BATCHING_OFF = "plugin.webresource.batching.off";
29  
30      private static final String DOWNLOAD_TYPE = "download";
31  
32      private static String[] BATCH_PARAMS = new String[] { "ieonly", "media", "content-type", "cache" };
33  
34      final private PluginAccessor pluginAccessor;
35      final private ServletContextFactory servletContextFactory;
36      private static final String RESOURCE_SOURCE_PARAM = "source";
37      private static final String RESOURCE_BATCH_PARAM = "batch";
38  
39      public PluginResourceLocatorImpl(PluginAccessor pluginAccessor, ServletContextFactory servletContextFactory)
40      {
41          this.pluginAccessor = pluginAccessor;
42          this.servletContextFactory = servletContextFactory;
43      }
44  
45      public boolean matches(String url)
46      {
47          return SinglePluginResource.matches(url) || BatchPluginResource.matches(url);
48      }
49  
50      public DownloadableResource getDownloadableResource(String url, Map<String, String> queryParams)
51      {
52          if (BatchPluginResource.matches(url))
53          {
54              BatchPluginResource batchResource = BatchPluginResource.parse(url, queryParams);
55              return locateBatchPluginResource(batchResource);
56          }
57  
58          if (SinglePluginResource.matches(url))
59          {
60              SinglePluginResource resource = SinglePluginResource.parse(url);
61              return locatePluginResource(resource.getModuleCompleteKey(), resource.getResourceName());
62          }
63  
64          log.error("Cannot locate resource for unknown url: " + url);
65          return null;
66      }
67  
68      private DownloadableResource locateBatchPluginResource(BatchPluginResource batchResource)
69      {
70          ModuleDescriptor moduleDescriptor = pluginAccessor.getEnabledPluginModule(batchResource.getModuleCompleteKey());
71          for (ResourceDescriptor resourceDescriptor : moduleDescriptor.getResourceDescriptors(DOWNLOAD_TYPE))
72          {
73              if (isResourceInBatch(resourceDescriptor, batchResource))
74                  batchResource.add(locatePluginResource(moduleDescriptor.getCompleteKey(), resourceDescriptor.getName()));
75          }
76  
77          // if batch is empty, check if we can locate a plugin resource
78          if (batchResource.isEmpty())
79          {
80              DownloadableResource resource = locatePluginResource(batchResource.getModuleCompleteKey(), batchResource.getResourceName());
81              if (resource != null)
82                  return resource;
83          }
84  
85          return batchResource;
86      }
87  
88      private boolean isResourceInBatch(ResourceDescriptor resourceDescriptor, BatchPluginResource batchResource)
89      {
90          if (!resourceDescriptor.getName().endsWith("." + batchResource.getType()))
91              return false;
92  
93          if (skipBatch(resourceDescriptor))
94              return false;
95  
96          for (String param : BATCH_PARAMS)
97          {
98              String batchValue = batchResource.getParams().get(param);
99              String resourceValue = resourceDescriptor.getParameter(param);
100 
101             if (batchValue == null && resourceValue != null)
102                 return false;
103 
104             if(batchValue != null && !batchValue.equals(resourceValue))
105                 return false;
106         }
107 
108         return true;
109     }
110 
111     private DownloadableResource locatePluginResource(String moduleCompleteKey, String resourceName)
112     {
113         DownloadableResource resource;
114 
115         // resource from the module
116         if (moduleCompleteKey.indexOf(":") > -1)
117         {
118             ModuleDescriptor moduleDescriptor = pluginAccessor.getPluginModule(moduleCompleteKey);
119             if (moduleDescriptor != null && pluginAccessor.isPluginModuleEnabled(moduleCompleteKey))
120             {
121                 resource = getResourceFromModule(moduleDescriptor, resourceName, "");
122             }
123             else
124             {
125                 log.info("Module not found: " + moduleCompleteKey);
126                 return null;
127             }
128         }
129         else // resource from plugin
130         {
131             Plugin plugin = pluginAccessor.getPlugin(moduleCompleteKey);
132             resource = getResourceFromPlugin(plugin, resourceName, "");
133         }
134 
135         if (resource == null)
136             resource = getResourceFromPlugin(getPlugin(moduleCompleteKey), resourceName, "");
137 
138         if (resource == null)
139         {
140             log.info("Unable to find resource for plugin: " + moduleCompleteKey + " and path: " + resourceName);
141             return null;
142         }
143 
144         return resource;
145     }
146 
147     private Plugin getPlugin(String moduleKey)
148     {
149         if (moduleKey.indexOf(':') < 0 || moduleKey.indexOf(':') == moduleKey.length() - 1)
150             return null;
151 
152         return pluginAccessor.getPlugin(moduleKey.substring(0, moduleKey.indexOf(':')));
153     }
154 
155     private DownloadableResource getResourceFromModule(ModuleDescriptor moduleDescriptor, String resourcePath, String filePath)
156     {
157         Plugin plugin = pluginAccessor.getPlugin(moduleDescriptor.getPluginKey());
158         ResourceLocation resourceLocation = moduleDescriptor.getResourceLocation(DOWNLOAD_TYPE, resourcePath);
159 
160         if (resourceLocation != null)
161         {
162             return getDownloadablePluginResource(plugin, resourceLocation, filePath);
163         }
164 
165         String[] nextParts = splitLastPathPart(resourcePath);
166         if (nextParts == null)
167             return null;
168 
169         return getResourceFromModule(moduleDescriptor, nextParts[0], nextParts[1] + filePath);
170     }
171 
172     private DownloadableResource getResourceFromPlugin(Plugin plugin, String resourcePath, String filePath)
173     {
174         if (plugin == null)
175             return null;
176 
177         ResourceLocation resourceLocation = plugin.getResourceLocation(DOWNLOAD_TYPE, resourcePath);
178         if (resourceLocation != null)
179         {
180             return getDownloadablePluginResource(plugin, resourceLocation, filePath);
181         }
182 
183         String[] nextParts = splitLastPathPart(resourcePath);
184         if (nextParts == null)
185             return null;
186 
187         return getResourceFromPlugin(plugin, nextParts[0], nextParts[1] + filePath);
188     }
189 
190     // pacakge protected so we can test it
191     String[] splitLastPathPart(String resourcePath)
192     {
193         int indexOfSlash = resourcePath.lastIndexOf('/');
194         if (resourcePath.endsWith("/")) // skip over the trailing slash
195         {
196             indexOfSlash = resourcePath.lastIndexOf('/', indexOfSlash - 1);
197         }
198 
199         if (indexOfSlash < 0) return null;
200 
201         return new String[] {
202             resourcePath.substring(0, indexOfSlash + 1),
203             resourcePath.substring(indexOfSlash + 1)
204         };
205     }
206 
207     private DownloadableResource getDownloadablePluginResource(Plugin plugin, ResourceLocation resourceLocation, String filePath)
208     {
209         String sourceParam = resourceLocation.getParameter(RESOURCE_SOURCE_PARAM);
210 
211         // serve by forwarding the request to the location - batching not supported
212         if("webContext".equalsIgnoreCase(sourceParam))
213             return new ForwardableResource(resourceLocation);
214 
215         // serve static resources from the web application - batching supported
216         if ("webContextStatic".equalsIgnoreCase(sourceParam))
217             return new DownloadableWebResource(plugin, resourceLocation, filePath, servletContextFactory.getServletContext());
218 
219         return new DownloadableClasspathResource(plugin, resourceLocation, filePath);
220     }
221 
222     public List<PluginResource> getPluginResources(String moduleCompleteKey)
223     {
224         ModuleDescriptor moduleDescriptor = pluginAccessor.getEnabledPluginModule(moduleCompleteKey);
225         if (moduleDescriptor == null || !(moduleDescriptor instanceof WebResourceModuleDescriptor))
226         {
227             log.error("Error loading resource \"" + moduleCompleteKey + "\". Resource is not a Web Resource Module");
228             return Collections.EMPTY_LIST;
229         }
230 
231         boolean singleMode = Boolean.valueOf(System.getProperty(PLUGIN_WEBRESOURCE_BATCHING_OFF));
232         List<PluginResource> resources = new ArrayList<PluginResource>();
233 
234         for (ResourceDescriptor resourceDescriptor : moduleDescriptor.getResourceDescriptors())
235         {
236             if (singleMode || skipBatch(resourceDescriptor))
237             {
238                 boolean cache = !"false".equalsIgnoreCase(resourceDescriptor.getParameter("cache"));
239                 resources.add(new SinglePluginResource(resourceDescriptor.getName(), moduleDescriptor.getCompleteKey(), cache));
240             }
241             else
242             {
243                 BatchPluginResource batchResource = createBatchResource(moduleDescriptor.getCompleteKey(),  resourceDescriptor);
244                 if (!resources.contains(batchResource))
245                     resources.add(batchResource);
246             }
247         }
248         return resources;
249     }
250 
251     private boolean skipBatch(ResourceDescriptor resourceDescriptor)
252     {
253         return "false".equalsIgnoreCase(resourceDescriptor.getParameter(RESOURCE_BATCH_PARAM)) ||
254             "webContext".equalsIgnoreCase(resourceDescriptor.getParameter(RESOURCE_SOURCE_PARAM)); // you can't batch forwarded requests
255     }
256 
257     private BatchPluginResource createBatchResource(String moduleCompleteKey, ResourceDescriptor resourceDescriptor)
258     {
259         String name = resourceDescriptor.getName();
260         String type = name.substring(name.lastIndexOf(".") + 1);
261         Map<String, String> params = new TreeMap<String, String>();
262         for (String param : BATCH_PARAMS)
263         {
264             String value = resourceDescriptor.getParameter(param);
265             if (StringUtils.isNotEmpty(value))
266                 params.put(param, value);
267         }
268 
269         return new BatchPluginResource(moduleCompleteKey, type, params);
270     }
271 
272     public String getResourceUrl(String moduleCompleteKey, String resourceName)
273     {
274         PluginResource pluginResource = new SinglePluginResource(resourceName, moduleCompleteKey, false);
275         return pluginResource.getUrl();
276     }
277 }