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
22
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
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
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
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
191 String[] splitLastPathPart(String resourcePath)
192 {
193 int indexOfSlash = resourcePath.lastIndexOf('/');
194 if (resourcePath.endsWith("/"))
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
212 if("webContext".equalsIgnoreCase(sourceParam))
213 return new ForwardableResource(resourceLocation);
214
215
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(),
240 cache, resourceDescriptor.getParameters()));
241 }
242 else
243 {
244 BatchPluginResource batchResource = createBatchResource(moduleDescriptor.getCompleteKey(), resourceDescriptor);
245 if (!resources.contains(batchResource))
246 resources.add(batchResource);
247 }
248 }
249 return resources;
250 }
251
252 private boolean skipBatch(ResourceDescriptor resourceDescriptor)
253 {
254 return "false".equalsIgnoreCase(resourceDescriptor.getParameter(RESOURCE_BATCH_PARAM)) ||
255 "webContext".equalsIgnoreCase(resourceDescriptor.getParameter(RESOURCE_SOURCE_PARAM));
256 }
257
258 private BatchPluginResource createBatchResource(String moduleCompleteKey, ResourceDescriptor resourceDescriptor)
259 {
260 String name = resourceDescriptor.getName();
261 String type = name.substring(name.lastIndexOf(".") + 1);
262 Map<String, String> params = new TreeMap<String, String>();
263 for (String param : BATCH_PARAMS)
264 {
265 String value = resourceDescriptor.getParameter(param);
266 if (StringUtils.isNotEmpty(value))
267 params.put(param, value);
268 }
269
270 return new BatchPluginResource(moduleCompleteKey, type, params);
271 }
272
273 public String getResourceUrl(String moduleCompleteKey, String resourceName)
274 {
275 PluginResource pluginResource = new SinglePluginResource(resourceName, moduleCompleteKey, false);
276 return pluginResource.getUrl();
277 }
278 }