1 package com.atlassian.plugin.webresource;
2
3 import com.atlassian.plugin.ModuleDescriptor;
4 import com.atlassian.plugin.Plugin;
5
6 import org.apache.commons.logging.Log;
7 import org.apache.commons.logging.LogFactory;
8
9 import java.io.IOException;
10 import java.io.StringWriter;
11 import java.io.Writer;
12 import java.util.Arrays;
13 import java.util.LinkedHashSet;
14 import java.util.List;
15 import java.util.Map;
16 import java.util.Stack;
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32 public class WebResourceManagerImpl implements WebResourceManager
33 {
34 private static final Log log = LogFactory.getLog(WebResourceManagerImpl.class);
35
36 static final String STATIC_RESOURCE_PREFIX = "s";
37 static final String STATIC_RESOURCE_SUFFIX = "_";
38
39 static final String REQUEST_CACHE_RESOURCE_KEY = "plugin.webresource.names";
40
41 protected final WebResourceIntegration webResourceIntegration;
42 protected final PluginResourceLocator pluginResourceLocator;
43 protected static final List<WebResourceFormatter> webResourceFormatters = Arrays.< WebResourceFormatter>asList(
44 new CssWebResourceFormatter(),
45 new JavascriptWebResourceFormatter()
46 );
47
48 public WebResourceManagerImpl(final PluginResourceLocator pluginResourceLocator, final WebResourceIntegration webResourceIntegration)
49 {
50 this.pluginResourceLocator = pluginResourceLocator;
51 this.webResourceIntegration = webResourceIntegration;
52 }
53
54 public void requireResource(final String moduleCompleteKey)
55 {
56 log.debug("Requiring resource: " + moduleCompleteKey);
57 final LinkedHashSet<String> webResourceNames = getWebResourceNames();
58
59 final LinkedHashSet<String> resources = new LinkedHashSet<String>();
60 addResourceWithDependencies(moduleCompleteKey, resources, new Stack<String>());
61 webResourceNames.addAll(resources);
62 }
63
64 private LinkedHashSet<String> getWebResourceNames()
65 {
66 final Map<String, Object> cache = webResourceIntegration.getRequestCache();
67 @SuppressWarnings("unchecked")
68 LinkedHashSet<String> webResourceNames = (LinkedHashSet<String>) cache.get(REQUEST_CACHE_RESOURCE_KEY);
69 if (webResourceNames == null)
70 {
71 webResourceNames = new LinkedHashSet<String>();
72 cache.put(REQUEST_CACHE_RESOURCE_KEY, webResourceNames);
73 }
74 return webResourceNames;
75 }
76
77
78
79
80
81
82
83
84
85
86 private void addResourceWithDependencies(final String moduleKey, final LinkedHashSet<String> orderedResourceKeys, final Stack<String> stack)
87 {
88 if (stack.contains(moduleKey))
89 {
90 log.warn("Cyclic plugin resource dependency has been detected with: " + moduleKey + "\n" +
91 "Stack trace: " + stack);
92 return;
93 }
94
95 final ModuleDescriptor<?> moduleDescriptor = webResourceIntegration.getPluginAccessor().getEnabledPluginModule(moduleKey);
96 if (!(moduleDescriptor instanceof WebResourceModuleDescriptor))
97 {
98 log.warn("Cannot find web resource module for: " + moduleKey);
99 return;
100 }
101
102 final List<String> dependencies = ((WebResourceModuleDescriptor) moduleDescriptor).getDependencies();
103 log.debug("About to add resource [" + moduleKey + "] and its dependencies: " + dependencies);
104
105 stack.push(moduleKey);
106 try
107 {
108 for (final String dependency : dependencies)
109 {
110 if (!orderedResourceKeys.contains(dependency))
111 {
112 addResourceWithDependencies(dependency, orderedResourceKeys, stack);
113 }
114 }
115 }
116 finally
117 {
118 stack.pop();
119 }
120 orderedResourceKeys.add(moduleKey);
121 }
122
123 public void includeResources(Writer writer)
124 {
125 includeResources(writer, true);
126 }
127
128 public String getRequiredResources()
129 {
130 StringWriter writer = new StringWriter();
131 includeResources(writer, false);
132 return writer.toString();
133 }
134
135 private void includeResources(Writer writer, boolean clearResources)
136 {
137 LinkedHashSet<String> webResourceNames = getWebResourceNames();
138 if (webResourceNames == null || webResourceNames.isEmpty())
139 {
140 log.debug("No resources required to write");
141 return;
142 }
143
144 for (Object webResourceName : webResourceNames)
145 {
146 String resourceName = (String) webResourceName;
147 writeResourceTag(resourceName, writer);
148 }
149 if (clearResources)
150 {
151 webResourceNames.clear();
152 }
153 }
154
155 public void requireResource(final String moduleCompleteKey, final Writer writer)
156 {
157 final LinkedHashSet<String> resourcesWithDeps = new LinkedHashSet<String>();
158 addResourceWithDependencies(moduleCompleteKey, resourcesWithDeps, new Stack<String>());
159
160 for (final Object resource : resourcesWithDeps)
161 {
162 writeResourceTag((String) resource, writer);
163 }
164 }
165
166 private void writeResourceTag(final String moduleCompleteKey, final Writer writer)
167 {
168 final List<PluginResource> resources = pluginResourceLocator.getPluginResources(moduleCompleteKey);
169
170 if (resources == null)
171 {
172 writeContentAndSwallowErrors("<!-- Error loading resource \"" + moduleCompleteKey + "\". Resource not found -->\n", writer);
173 return;
174 }
175
176 for (final PluginResource resource : resources)
177 {
178 final WebResourceFormatter formatter = getWebResourceFormatter(resource.getResourceName());
179 if (formatter == null)
180 {
181 writeContentAndSwallowErrors("<!-- Error loading resource \"" + moduleCompleteKey + "\". Resource formatter not found -->\n", writer);
182 continue;
183 }
184
185 String url = resource.getUrl();
186 if (resource.isCacheSupported())
187 {
188 final Plugin plugin = webResourceIntegration.getPluginAccessor().getEnabledPluginModule(resource.getModuleCompleteKey()).getPlugin();
189 url = getStaticResourcePrefix(plugin.getPluginInformation().getVersion()) + url;
190 }
191 else
192 {
193 url = webResourceIntegration.getBaseUrl() + url;
194 }
195 writeContentAndSwallowErrors(formatter.formatResource(url, resource.getParams()), writer);
196 }
197 }
198
199 public String getResourceTags(final String moduleCompleteKey)
200 {
201 final StringWriter writer = new StringWriter();
202 requireResource(moduleCompleteKey, writer);
203 return writer.toString();
204 }
205
206 private void writeContentAndSwallowErrors(final String content, final Writer writer)
207 {
208 try
209 {
210 writer.write(content);
211 }
212 catch (final IOException e)
213 {
214 log.error(e);
215 }
216 }
217
218 private WebResourceFormatter getWebResourceFormatter(final String resourceName)
219 {
220 for (final WebResourceFormatter webResourceFormatter : webResourceFormatters)
221 {
222 if (webResourceFormatter.matches(resourceName))
223 {
224 return webResourceFormatter;
225 }
226 }
227 return null;
228 }
229
230 public String getStaticResourcePrefix()
231 {
232
233 return webResourceIntegration.getBaseUrl() + "/" +
234 STATIC_RESOURCE_PREFIX + "/" +
235 webResourceIntegration.getSystemBuildNumber() + "/" +
236 webResourceIntegration.getSystemCounter() + "/" +
237 STATIC_RESOURCE_SUFFIX;
238 }
239
240 public String getStaticResourcePrefix(final String resourceCounter)
241 {
242
243 return webResourceIntegration.getBaseUrl() + "/" +
244 STATIC_RESOURCE_PREFIX + "/" +
245 webResourceIntegration.getSystemBuildNumber() + "/" +
246 webResourceIntegration.getSystemCounter() + "/" +
247 resourceCounter + "/" +
248 STATIC_RESOURCE_SUFFIX;
249 }
250
251 public String getStaticPluginResource(final String moduleCompleteKey, final String resourceName)
252 {
253 final ModuleDescriptor<?> moduleDescriptor = webResourceIntegration.getPluginAccessor().getEnabledPluginModule(moduleCompleteKey);
254 if(moduleDescriptor == null)
255 {
256 return null;
257 }
258
259 return getStaticPluginResource(moduleDescriptor, resourceName);
260 }
261
262
263
264
265 @SuppressWarnings("unchecked")
266 public String getStaticPluginResource(final ModuleDescriptor moduleDescriptor, final String resourceName)
267 {
268
269 final String staticUrlPrefix = getStaticResourcePrefix(String.valueOf(moduleDescriptor.getPlugin().getPluginsVersion()));
270
271 return staticUrlPrefix + pluginResourceLocator.getResourceUrl(moduleDescriptor.getCompleteKey(), resourceName);
272 }
273
274
275
276
277
278
279 @Deprecated
280 @SuppressWarnings("unchecked")
281 public String getStaticPluginResourcePrefix(final ModuleDescriptor moduleDescriptor, final String resourceName)
282 {
283 return getStaticPluginResource(moduleDescriptor, resourceName);
284 }
285
286
287
288
289 @Deprecated
290 private static final String REQUEST_CACHE_MODE_KEY = "plugin.webresource.mode";
291
292
293
294
295 @Deprecated
296 private static final IncludeMode DEFAULT_INCLUDE_MODE = WebResourceManager.DELAYED_INCLUDE_MODE;
297
298
299
300
301 @Deprecated
302 public void setIncludeMode(final IncludeMode includeMode)
303 {
304 webResourceIntegration.getRequestCache().put(REQUEST_CACHE_MODE_KEY, includeMode);
305 }
306
307 IncludeMode getIncludeMode()
308 {
309 IncludeMode includeMode = (IncludeMode) webResourceIntegration.getRequestCache().get(REQUEST_CACHE_MODE_KEY);
310 if (includeMode == null)
311 {
312 includeMode = DEFAULT_INCLUDE_MODE;
313 }
314 return includeMode;
315 }
316 }