1 package com.atlassian.plugin.webresource;
2
3 import com.atlassian.plugin.ModuleDescriptor;
4
5 import java.io.IOException;
6 import java.io.StringWriter;
7 import java.io.Writer;
8 import java.util.*;
9
10 import org.slf4j.Logger;
11 import org.slf4j.LoggerFactory;
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27 public class WebResourceManagerImpl implements WebResourceManager
28 {
29 private static final Logger log = LoggerFactory.getLogger(WebResourceManagerImpl.class);
30
31 static final String STATIC_RESOURCE_PREFIX = "s";
32 static final String STATIC_RESOURCE_SUFFIX = "_";
33
34 static final String REQUEST_CACHE_RESOURCE_KEY = "plugin.webresource.names";
35
36 protected final WebResourceIntegration webResourceIntegration;
37 protected final PluginResourceLocator pluginResourceLocator;
38 protected final ResourceBatchingConfiguration batchingConfiguration;
39 protected final ResourceDependencyResolver dependencyResolver;
40 protected static final List<WebResourceFormatter> webResourceFormatters = Arrays.asList(CssWebResource.FORMATTER, JavascriptWebResource.FORMATTER);
41
42 private static final boolean IGNORE_SUPERBATCHING = false;
43
44 public WebResourceManagerImpl(PluginResourceLocator pluginResourceLocator, WebResourceIntegration webResourceIntegration)
45 {
46 this(pluginResourceLocator, webResourceIntegration, new DefaultResourceBatchingConfiguration());
47 }
48
49 public WebResourceManagerImpl(PluginResourceLocator pluginResourceLocator, WebResourceIntegration webResourceIntegration, ResourceBatchingConfiguration batchingConfiguration)
50 {
51 this(pluginResourceLocator, webResourceIntegration, batchingConfiguration, new DefaultResourceDependencyResolver(webResourceIntegration, batchingConfiguration));
52 }
53
54 public WebResourceManagerImpl(PluginResourceLocator pluginResourceLocator, WebResourceIntegration webResourceIntegration,
55 ResourceBatchingConfiguration batchingConfiguration, ResourceDependencyResolver dependencyResolver)
56 {
57 this.pluginResourceLocator = pluginResourceLocator;
58 this.webResourceIntegration = webResourceIntegration;
59 this.batchingConfiguration = batchingConfiguration;
60 this.dependencyResolver = dependencyResolver;
61 }
62
63 public void requireResource(String moduleCompleteKey)
64 {
65 log.debug("Requiring resource: " + moduleCompleteKey);
66 getIncludedResourceNames().addAll(dependencyResolver.getDependencies(moduleCompleteKey, batchingConfiguration.isSuperBatchingEnabled()));
67 }
68
69 public void requireResourcesForContext(String context)
70 {
71 final List<WebResourceModuleDescriptor> webResourceModuleDescriptors =
72 webResourceIntegration.getPluginAccessor().getEnabledModuleDescriptorsByClass(WebResourceModuleDescriptor.class);
73
74 for (WebResourceModuleDescriptor webResourceModuleDescriptor : webResourceModuleDescriptors)
75 {
76 if (webResourceModuleDescriptor.getContexts().contains(context))
77 {
78 requireResource(webResourceModuleDescriptor.getCompleteKey());
79 }
80 }
81 }
82
83 private LinkedHashSet<String> getIncludedResourceNames()
84 {
85 final Map<String, Object> cache = webResourceIntegration.getRequestCache();
86 @SuppressWarnings("unchecked")
87 LinkedHashSet<String> webResourceNames = (LinkedHashSet<String>) cache.get(REQUEST_CACHE_RESOURCE_KEY);
88 if (webResourceNames == null)
89 {
90 webResourceNames = new LinkedHashSet<String>();
91 cache.put(REQUEST_CACHE_RESOURCE_KEY, webResourceNames);
92 }
93 return webResourceNames;
94 }
95
96 private void clearIncludedResourceNames()
97 {
98 log.debug("Clearing included resources");
99 getIncludedResourceNames().clear();
100 }
101
102
103
104
105
106
107
108 public void includeResources(Writer writer)
109 {
110 includeResources(writer, UrlMode.AUTO);
111 }
112
113 public void includeResources(Iterable<String> moduleCompleteKeys, Writer writer, UrlMode urlMode)
114 {
115 LinkedHashSet<String> resources = new LinkedHashSet<String>();
116 for (String moduleCompleteKey : moduleCompleteKeys)
117 {
118
119 Set<String> dependencies = dependencyResolver.getDependencies(moduleCompleteKey, false);
120 resources.addAll(dependencies);
121 }
122 writeResourceTags(getModuleResources(resources, DefaultWebResourceFilter.INSTANCE), writer, urlMode);
123 }
124
125
126
127
128
129
130
131 public void includeResources(Writer writer, UrlMode urlMode)
132 {
133 includeResources(writer, urlMode, DefaultWebResourceFilter.INSTANCE);
134 }
135
136
137
138
139
140
141
142
143
144
145 public void includeResources(Writer writer, UrlMode urlMode, WebResourceFilter webResourceFilter)
146 {
147 writeIncludedResources(writer, urlMode, webResourceFilter);
148 clearIncludedResourceNames();
149 }
150
151
152
153
154
155
156
157 public String getRequiredResources()
158 {
159 return getRequiredResources(UrlMode.AUTO);
160 }
161
162
163
164
165
166
167
168 public String getRequiredResources(UrlMode urlMode)
169 {
170 return getRequiredResources(urlMode, DefaultWebResourceFilter.INSTANCE);
171 }
172
173
174
175
176
177
178
179
180
181
182
183 public String getRequiredResources(UrlMode urlMode, WebResourceFilter webResourceFilter)
184 {
185 final StringWriter writer = new StringWriter();
186 writeIncludedResources(writer, urlMode, webResourceFilter);
187 return writer.toString();
188 }
189
190
191
192
193 private void writeIncludedResources(Writer writer, UrlMode urlMode, WebResourceFilter filter)
194 {
195 List<PluginResource> resourcesToInclude = new ArrayList<PluginResource>();
196 resourcesToInclude.addAll(getSuperBatchResources(filter));
197 resourcesToInclude.addAll(getModuleResources(getIncludedResourceNames(), filter));
198
199 writeResourceTags(resourcesToInclude, writer, urlMode);
200 }
201
202
203
204
205
206
207
208 List<PluginResource> getSuperBatchResources(WebResourceFilter filter)
209 {
210 if (!batchingConfiguration.isSuperBatchingEnabled())
211 return Collections.emptyList();
212
213 LinkedHashSet<String> superBatchModuleKeys = dependencyResolver.getSuperBatchDependencies();
214 List<PluginResource> resources = new ArrayList<PluginResource>();
215
216
217
218
219
220 for (WebResourceFormatter formatter : webResourceFormatters)
221 {
222 Set<Map<String, String>> alreadyIncluded = new HashSet<Map<String, String>>();
223 for (String moduleKey : superBatchModuleKeys)
224 {
225 for (PluginResource pluginResource : pluginResourceLocator.getPluginResources(moduleKey))
226 {
227 if (formatter.matches(pluginResource.getResourceName()) && filter.matches(pluginResource.getResourceName()))
228 {
229 Map<String, String> batchParamsMap = new HashMap<String, String>(PluginResourceLocator.BATCH_PARAMS.length);
230 for (String s : PluginResourceLocator.BATCH_PARAMS)
231 {
232 batchParamsMap.put(s, pluginResource.getParams().get(s));
233 }
234
235 if (!alreadyIncluded.contains(batchParamsMap))
236 {
237 resources.add(SuperBatchPluginResource.createBatchFor(pluginResource));
238 alreadyIncluded.add(batchParamsMap);
239 }
240 }
241 }
242 }
243 }
244 return resources;
245 }
246
247 private List<PluginResource> getModuleResources(LinkedHashSet<String> webResourcePluginModuleKeys, WebResourceFilter filter)
248 {
249 List<PluginResource> includedResources = new ArrayList<PluginResource>();
250 for (String moduleKey: webResourcePluginModuleKeys)
251 {
252 List<PluginResource> moduleResources = pluginResourceLocator.getPluginResources(moduleKey);
253 for (PluginResource moduleResource : moduleResources)
254 {
255 if (filter.matches(moduleResource.getResourceName()))
256 includedResources.add(moduleResource);
257 else
258 log.debug("Resource [" + moduleResource.getResourceName() + "] excluded by filter");
259 }
260 }
261 return includedResources;
262 }
263
264
265
266
267
268 private void writeResourceTags(List<PluginResource> resourcesToInclude, Writer writer, UrlMode urlMode)
269 {
270 for (WebResourceFormatter formatter : webResourceFormatters)
271 {
272 for (Iterator<PluginResource> iter = resourcesToInclude.iterator(); iter.hasNext();)
273 {
274 PluginResource resource = iter.next();
275 if (formatter.matches(resource.getResourceName()))
276 {
277 writeResourceTag(urlMode, resource, formatter, writer);
278 iter.remove();
279 }
280 }
281 }
282
283 for (PluginResource resource : resourcesToInclude)
284 {
285 writeContentAndSwallowErrors("<!-- Error loading resource \"" + resource.getModuleCompleteKey() + "\". No resource formatter matches \"" + resource.getResourceName() + "\" -->\n",
286 writer);
287 }
288 }
289
290 private void writeResourceTag(UrlMode urlMode, PluginResource resource, WebResourceFormatter formatter, Writer writer)
291 {
292 String url = resource.getUrl();
293 if (resource.isCacheSupported())
294 {
295 url = getStaticResourcePrefix(resource.getVersion(webResourceIntegration), urlMode) + url;
296 }
297 else
298 {
299 url = webResourceIntegration.getBaseUrl(urlMode) + url;
300 }
301 writeContentAndSwallowErrors(formatter.formatResource(url, resource.getParams()), writer);
302 }
303
304 public void requireResource(String moduleCompleteKey, Writer writer)
305 {
306 requireResource(moduleCompleteKey, writer, UrlMode.AUTO);
307 }
308
309 public void requireResource(String moduleCompleteKey, Writer writer, UrlMode urlMode)
310 {
311 LinkedHashSet<String> allDependentModuleKeys = dependencyResolver.getDependencies(moduleCompleteKey, IGNORE_SUPERBATCHING);
312 List<PluginResource> resourcesToInclude = getModuleResources(allDependentModuleKeys, DefaultWebResourceFilter.INSTANCE);
313 writeResourceTags(resourcesToInclude, writer, urlMode);
314 }
315
316 public String getResourceTags(String moduleCompleteKey)
317 {
318 return getResourceTags(moduleCompleteKey, UrlMode.AUTO);
319 }
320
321 public String getResourceTags(String moduleCompleteKey, UrlMode urlMode)
322 {
323 final StringWriter writer = new StringWriter();
324 requireResource(moduleCompleteKey, writer, urlMode);
325 return writer.toString();
326 }
327
328 private void writeContentAndSwallowErrors(String content, Writer writer)
329 {
330 try
331 {
332 writer.write(content);
333 }
334 catch (final IOException e)
335 {
336 log.debug("Ignoring", e);
337 }
338 }
339
340 public String getStaticResourcePrefix()
341 {
342 return getStaticResourcePrefix(UrlMode.AUTO);
343 }
344
345 public String getStaticResourcePrefix(UrlMode urlMode)
346 {
347
348 return webResourceIntegration.getBaseUrl(urlMode) + "/" + STATIC_RESOURCE_PREFIX + "/" + webResourceIntegration.getSystemBuildNumber() + "/" + webResourceIntegration.getSystemCounter() + "/" + STATIC_RESOURCE_SUFFIX;
349 }
350
351 public String getStaticResourcePrefix(String resourceCounter)
352 {
353 return getStaticResourcePrefix(resourceCounter, UrlMode.AUTO);
354 }
355
356 public String getStaticResourcePrefix(String resourceCounter, UrlMode urlMode)
357 {
358
359 return webResourceIntegration.getBaseUrl(urlMode) + "/" + STATIC_RESOURCE_PREFIX + "/" + webResourceIntegration.getSystemBuildNumber() + "/" + webResourceIntegration.getSystemCounter() + "/" + resourceCounter + "/" + STATIC_RESOURCE_SUFFIX;
360 }
361
362 public String getStaticPluginResource(final String moduleCompleteKey, final String resourceName)
363 {
364 return getStaticPluginResource(moduleCompleteKey, resourceName, UrlMode.AUTO);
365 }
366
367 public String getStaticPluginResource(final String moduleCompleteKey, final String resourceName, final UrlMode urlMode)
368 {
369 final ModuleDescriptor<?> moduleDescriptor = webResourceIntegration.getPluginAccessor().getEnabledPluginModule(moduleCompleteKey);
370 if (moduleDescriptor == null)
371 {
372 return null;
373 }
374
375 return getStaticPluginResource(moduleDescriptor, resourceName, urlMode);
376 }
377
378
379
380
381 @SuppressWarnings("unchecked")
382 public String getStaticPluginResource(ModuleDescriptor moduleDescriptor, String resourceName)
383 {
384 return getStaticPluginResource(moduleDescriptor, resourceName, UrlMode.AUTO);
385 }
386
387 public String getStaticPluginResource(ModuleDescriptor moduleDescriptor, String resourceName, UrlMode urlMode)
388 {
389
390 final String staticUrlPrefix = getStaticResourcePrefix(String.valueOf(moduleDescriptor.getPlugin().getPluginsVersion()), urlMode);
391
392 return staticUrlPrefix + pluginResourceLocator.getResourceUrl(moduleDescriptor.getCompleteKey(), resourceName);
393 }
394
395
396
397
398
399
400 @Deprecated
401 @SuppressWarnings("unchecked")
402 public String getStaticPluginResourcePrefix(ModuleDescriptor moduleDescriptor, String resourceName)
403 {
404 return getStaticPluginResource(moduleDescriptor, resourceName);
405 }
406
407
408
409
410 @Deprecated
411 private static final String REQUEST_CACHE_MODE_KEY = "plugin.webresource.mode";
412
413
414
415
416 @Deprecated
417 private static final IncludeMode DEFAULT_INCLUDE_MODE = WebResourceManager.DELAYED_INCLUDE_MODE;
418
419
420
421
422 @Deprecated
423 public void setIncludeMode(final IncludeMode includeMode)
424 {
425 webResourceIntegration.getRequestCache().put(REQUEST_CACHE_MODE_KEY, includeMode);
426 }
427 }