1 package com.atlassian.plugin.webresource;
2
3 import java.util.Map;
4
5 import com.atlassian.plugin.ModuleDescriptor;
6 import com.atlassian.plugin.Plugin;
7 import com.atlassian.plugin.PluginAccessor;
8 import com.atlassian.plugin.elements.ResourceLocation;
9 import com.atlassian.plugin.servlet.DownloadableClasspathResource;
10 import com.atlassian.plugin.servlet.DownloadableResource;
11 import com.atlassian.plugin.servlet.DownloadableWebResource;
12 import com.atlassian.plugin.servlet.ForwardableResource;
13 import com.atlassian.plugin.servlet.ServletContextFactory;
14
15 import org.apache.commons.lang.text.StrSubstitutor;
16 import org.slf4j.Logger;
17 import org.slf4j.LoggerFactory;
18
19 import static com.atlassian.plugin.webresource.PluginResourceLocatorImpl.RESOURCE_SOURCE_PARAM;
20 import static com.atlassian.plugin.webresource.SinglePluginResource.URL_PREFIX;
21
22
23
24
25
26 public class SingleDownloadableResourceBuilder implements DownloadableResourceBuilder, DownloadableResourceFinder
27 {
28 private static final String DOWNLOAD_TYPE = "download";
29 private static final Logger log = LoggerFactory.getLogger(SingleDownloadableResourceBuilder.class);
30
31 private final PluginAccessor pluginAccessor;
32 private final ServletContextFactory servletContextFactory;
33 private final WebResourceIntegration webResourceIntegration;
34
35 public SingleDownloadableResourceBuilder(final PluginAccessor pluginAccessor, final ServletContextFactory servletContextFactory,
36 final WebResourceIntegration webResourceIntegration)
37 {
38 this.pluginAccessor = pluginAccessor;
39 this.servletContextFactory = servletContextFactory;
40 this.webResourceIntegration = webResourceIntegration;
41 }
42
43 public boolean matches(final String path)
44 {
45 return path.indexOf(URL_PREFIX) != -1;
46 }
47
48 public DownloadableResource parse(final String path, final Map<String, String> params) throws UrlParseException
49 {
50 final int indexOfPrefix = path.indexOf(URL_PREFIX);
51 String libraryAndResource = path.substring(indexOfPrefix + URL_PREFIX.length() + 1);
52
53 if (libraryAndResource.indexOf('?') != -1)
54 {
55 libraryAndResource = libraryAndResource.substring(0, libraryAndResource.indexOf('?'));
56 }
57
58 final String[] parts = libraryAndResource.split("/", 2);
59
60 if (parts.length != 2)
61 {
62 throw new UrlParseException("Could not parse invalid plugin resource url: " + path);
63 }
64
65 final PluginResource resource = new SinglePluginResource(parts[1], parts[0], path.substring(0, indexOfPrefix).length() > 0);
66
67 return find(resource.getModuleCompleteKey(), resource.getResourceName());
68 }
69
70 public DownloadableResource find(final String moduleKey, final String resourceName)
71 {
72 return locatePluginResource(moduleKey, resourceName);
73 }
74
75 private DownloadableResource locatePluginResource(final String moduleCompleteKey, final String resourceName)
76 {
77 DownloadableResource resource;
78
79
80 if (moduleCompleteKey.indexOf(":") > -1)
81 {
82 final ModuleDescriptor<?> moduleDescriptor = pluginAccessor.getEnabledPluginModule(moduleCompleteKey);
83 if (moduleDescriptor != null)
84 {
85 resource = getResourceFromModule(moduleDescriptor, resourceName, "");
86 }
87 else
88 {
89 log.info("Module not found: " + moduleCompleteKey);
90 return null;
91 }
92 }
93 else
94
95 {
96 resource = getResourceFromPlugin(pluginAccessor.getPlugin(moduleCompleteKey), resourceName, "");
97 }
98
99 if (resource == null)
100 {
101 resource = getResourceFromPlugin(getPlugin(moduleCompleteKey), resourceName, "");
102 }
103
104 if (resource == null)
105 {
106 log.info("Unable to find resource for plugin: " + moduleCompleteKey + " and path: " + resourceName);
107 return null;
108 }
109
110 return resource;
111 }
112
113 private Plugin getPlugin(final String moduleKey)
114 {
115 final int semicolonIndex = moduleKey.indexOf(':');
116 if ((semicolonIndex < 0) || (semicolonIndex == moduleKey.length() - 1))
117 {
118 return null;
119 }
120
121 return pluginAccessor.getPlugin(moduleKey.substring(0, semicolonIndex));
122 }
123
124 private DownloadableResource getResourceFromModule(final ModuleDescriptor<?> moduleDescriptor, final String resourcePath, final String filePath)
125 {
126 final Plugin plugin = pluginAccessor.getPlugin(moduleDescriptor.getPluginKey());
127 final ResourceLocation resourceLocation = moduleDescriptor.getResourceLocation(DOWNLOAD_TYPE, resourcePath);
128
129 if (resourceLocation != null)
130 {
131 boolean disableMinification = false;
132
133
134 if (moduleDescriptor instanceof WebResourceModuleDescriptor)
135 {
136 disableMinification = ((WebResourceModuleDescriptor) moduleDescriptor).isDisableMinification();
137 }
138 return getDownloadablePluginResource(plugin, resourceLocation, moduleDescriptor, filePath, disableMinification);
139 }
140
141 final String[] nextParts = splitLastPathPart(resourcePath);
142 if (nextParts == null)
143 {
144 return null;
145 }
146
147 return getResourceFromModule(moduleDescriptor, nextParts[0], nextParts[1] + filePath);
148 }
149
150 private DownloadableResource getResourceFromPlugin(final Plugin plugin, final String resourcePath, final String filePath)
151 {
152 if (plugin == null)
153 {
154 return null;
155 }
156
157 final ResourceLocation resourceLocation = plugin.getResourceLocation(DOWNLOAD_TYPE, resourcePath);
158 if (resourceLocation != null)
159 {
160 return getDownloadablePluginResource(plugin, resourceLocation, null, filePath, false);
161 }
162
163 final String[] nextParts = splitLastPathPart(resourcePath);
164 if (nextParts == null)
165 {
166 return null;
167 }
168
169 return getResourceFromPlugin(plugin, nextParts[0], nextParts[1] + filePath);
170 }
171
172 private DownloadableResource getDownloadablePluginResource(final Plugin plugin, final ResourceLocation originalResourceLocation, final ModuleDescriptor<?> descriptor, final String filePath, final boolean disableMinification)
173 {
174 ResourceLocation resourceLocation = substituteVariables(originalResourceLocation);
175 final String sourceParam = resourceLocation.getParameter(RESOURCE_SOURCE_PARAM);
176
177
178
179 if ("webContext".equalsIgnoreCase(sourceParam))
180 {
181 return new ForwardableResource(resourceLocation);
182 }
183
184 DownloadableResource actualResource;
185
186 if ("webContextStatic".equalsIgnoreCase(sourceParam))
187 {
188 actualResource = new DownloadableWebResource(plugin, resourceLocation, filePath, servletContextFactory.getServletContext(),
189 disableMinification);
190 }
191 else
192 {
193 actualResource = new DownloadableClasspathResource(plugin, resourceLocation, filePath);
194 }
195
196 DownloadableResource result = actualResource;
197
198 if (descriptor instanceof WebResourceModuleDescriptor)
199 {
200 DownloadableResource lastResource = actualResource;
201 final WebResourceModuleDescriptor desc = (WebResourceModuleDescriptor) descriptor;
202 for (final WebResourceTransformation list : desc.getTransformations())
203 {
204 if (list.matches(resourceLocation))
205 {
206 lastResource = list.transformDownloadableResource(pluginAccessor, actualResource, resourceLocation, filePath);
207 }
208 }
209 result = lastResource;
210 }
211 return result;
212 }
213
214 private ResourceLocation substituteVariables(ResourceLocation in)
215 {
216 String location = in.getLocation();
217 if (!location.contains("$"))
218 {
219 return in;
220 }
221
222 String newLocation = substituteVariables(location);
223 return new ResourceLocation(newLocation, in.getName(), in.getType(), in.getContentType(), in.getContent(), in.getParams());
224 }
225
226 private String substituteVariables(String location)
227 {
228 return StrSubstitutor.replace(location,
229 webResourceIntegration.getResourceSubstitutionVariables());
230 }
231
232
233 String[] splitLastPathPart(final String resourcePath)
234 {
235 int indexOfSlash = resourcePath.lastIndexOf('/');
236 if (resourcePath.endsWith("/"))
237 {
238 indexOfSlash = resourcePath.lastIndexOf('/', indexOfSlash - 1);
239 }
240
241 if (indexOfSlash < 0)
242 {
243 return null;
244 }
245
246 return new String[] { resourcePath.substring(0, indexOfSlash + 1), resourcePath.substring(indexOfSlash + 1) };
247 }
248 }