1 package com.atlassian.plugin.webresource;
2
3 import static com.atlassian.plugin.servlet.AbstractFileServerServlet.PATH_SEPARATOR;
4 import static com.atlassian.plugin.servlet.AbstractFileServerServlet.SERVLET_PATH;
5 import static com.google.common.base.Suppliers.ofInstance;
6 import static com.google.common.collect.ImmutableList.copyOf;
7 import static com.google.common.collect.Iterables.any;
8
9 import com.atlassian.plugin.Plugin;
10 import com.atlassian.plugin.servlet.DownloadException;
11 import com.atlassian.plugin.servlet.DownloadableResource;
12
13 import org.slf4j.Logger;
14 import org.slf4j.LoggerFactory;
15
16 import com.google.common.base.Predicate;
17 import com.google.common.collect.ImmutableMap;
18 import com.google.common.collect.Iterables;
19
20 import java.io.IOException;
21 import java.io.OutputStream;
22 import java.io.UnsupportedEncodingException;
23 import java.net.URLEncoder;
24 import java.util.Collections;
25 import java.util.Map;
26
27 import javax.servlet.http.HttpServletRequest;
28 import javax.servlet.http.HttpServletResponse;
29
30
31
32
33
34
35
36
37
38
39
40 public class BatchPluginResource implements DownloadableResource, PluginResource, BatchResource
41 {
42 private static final Logger log = LoggerFactory.getLogger(BatchPluginResource.class);
43
44 private static final ResourceContentAnnotator[] DEFAULT_ANNOTATORS = new ResourceContentAnnotator[] {
45 new NewlineResourceContentAnnotator()
46 };
47
48 private static final ResourceContentAnnotator[] JS_WRAP_ANNOTATORS = new ResourceContentAnnotator[] {
49 new NewlineResourceContentAnnotator(), new TryCatchJsResourceContentAnnotator()
50 };
51
52
53
54
55 static final String URL_PREFIX = PATH_SEPARATOR + SERVLET_PATH + PATH_SEPARATOR + "batch";
56
57 private final ResourceKey resource;
58 private final Map<String, String> params;
59 private final Iterable<DownloadableResource> resources;
60
61
62 private int hash = 0;
63
64
65
66
67
68
69
70
71
72
73
74 public BatchPluginResource(final String moduleCompleteKey, final String type, final Map<String, String> params)
75 {
76 this(ResourceKey.Builder.lazy(moduleCompleteKey, ofInstance(type)), params, Collections.<DownloadableResource>emptyList());
77 }
78
79
80
81
82
83
84
85
86
87
88
89
90
91 public BatchPluginResource(final String moduleCompleteKey, final String type, final Map<String, String> params,
92 final Iterable<DownloadableResource> resources)
93 {
94 this(ResourceKey.Builder.lazy(moduleCompleteKey, ofInstance(type)), params, resources);
95 }
96
97
98
99
100
101
102
103
104
105 BatchPluginResource(final ResourceKey resource, final Map<String, String> params,
106 final Iterable<DownloadableResource> resources)
107 {
108 this.resource = resource;
109 this.params = ImmutableMap.copyOf(params);
110 this.resources = copyOf(resources);
111 }
112
113
114
115
116 public boolean isEmpty()
117 {
118 return Iterables.isEmpty(resources);
119 }
120
121 public boolean isResourceModified(final HttpServletRequest request, final HttpServletResponse response)
122 {
123 return any(resources, new Predicate<DownloadableResource>()
124 {
125 public boolean apply(final DownloadableResource resource)
126 {
127 return resource.isResourceModified(request, response);
128 }
129 });
130 }
131
132 public void serveResource(final HttpServletRequest request, final HttpServletResponse response)
133 throws DownloadException
134 {
135 if (log.isDebugEnabled())
136 {
137 log.debug("Start to serve batch " + toString());
138 }
139
140 ResourceContentAnnotator[] annotators = getAnnotators(request);
141 for (final DownloadableResource resource : resources)
142 {
143 try
144 {
145 applyBeforeAnnotators(response.getOutputStream(), annotators);
146 resource.serveResource(request, response);
147 applyAfterAnnotators(response.getOutputStream(), annotators);
148 }
149 catch (IOException ex)
150 {
151 throw new DownloadException(ex);
152 }
153 }
154 }
155
156 public void streamResource(final OutputStream out) throws DownloadException
157 {
158 ResourceContentAnnotator[] annotators = getAnnotators(null);
159
160 for (final DownloadableResource resource : resources)
161 {
162 try
163 {
164 applyBeforeAnnotators(out, annotators);
165 resource.streamResource(out);
166 applyAfterAnnotators(out, annotators);
167 }
168 catch (IOException ex)
169 {
170 throw new DownloadException(ex);
171 }
172 }
173 }
174
175 public String getContentType()
176 {
177 final String contentType = params.get("content-type");
178 if (contentType != null)
179 {
180 return contentType;
181 }
182 return null;
183 }
184
185
186
187
188
189
190
191
192
193
194
195 public String getUrl()
196 {
197 final StringBuilder sb = new StringBuilder();
198 sb.append(URL_PREFIX).append(PATH_SEPARATOR).append(resource.key()).append(PATH_SEPARATOR).append(
199 resource.name());
200 addParamsToUrl(sb, params);
201 return sb.toString();
202 }
203
204 protected void addParamsToUrl(final StringBuilder sb, final Map<String, String> params)
205 {
206 if (params.size() > 0)
207 {
208 sb.append("?");
209 int count = 0;
210
211 for (final Map.Entry<String, String> entry : params.entrySet())
212 {
213 try
214 {
215 sb.append(URLEncoder.encode(entry.getKey(), "UTF-8")).append("=").append(URLEncoder.encode(
216 entry.getValue(), "UTF-8"));
217
218 if (++count < params.size())
219 {
220 sb.append("&");
221 }
222 }
223 catch (final UnsupportedEncodingException e)
224 {
225 log.error("Could not encode parameter to url for [" + entry.getKey() + "] with value [" +
226 entry.getValue() + "]", e);
227 }
228 }
229 }
230 }
231
232 public String getResourceName()
233 {
234 return resource.name();
235 }
236
237 public Map<String, String> getParams()
238 {
239 return params;
240 }
241
242 public String getVersion(final WebResourceIntegration integration)
243 {
244 final Plugin plugin = integration.getPluginAccessor().getEnabledPluginModule(
245 getModuleCompleteKey()).getPlugin();
246 return plugin.getPluginInformation().getVersion();
247 }
248
249 public String getModuleCompleteKey()
250 {
251 return resource.key();
252 }
253
254 public boolean isCacheSupported()
255 {
256 return !"false".equals(params.get("cache"));
257 }
258
259 @Override
260 public String getType()
261 {
262 return resource.suffix();
263 }
264
265 private void applyBeforeAnnotators(OutputStream str, ResourceContentAnnotator[] annotators) throws IOException
266 {
267 for (ResourceContentAnnotator annotator : annotators)
268 {
269 annotator.before(str);
270 }
271 }
272
273
274
275
276
277
278
279 private void applyAfterAnnotators(OutputStream str, ResourceContentAnnotator[] annotators) throws IOException
280 {
281 for (int i = annotators.length - 1; i >= 0; i--)
282 {
283 annotators[i].after(str);
284 }
285 }
286
287 @Override
288 public boolean equals(final Object o)
289 {
290 if (this == o)
291 {
292 return true;
293 }
294 if ((o == null) || (getClass() != o.getClass()))
295 {
296 return false;
297 }
298
299 final BatchPluginResource that = (BatchPluginResource) o;
300
301 if (params != null ? !params.equals(that.params) : that.params != null)
302 {
303 return false;
304 }
305 return resource.equals(that.resource);
306 }
307
308 @Override
309 public int hashCode()
310 {
311 if (hash == 0)
312 {
313 int result;
314 result = resource.hashCode();
315 result = 31 * result + (params != null ? params.hashCode() : 0);
316 hash = result;
317 }
318 return hash;
319 }
320
321 @Override
322 public String toString()
323 {
324 return "[moduleCompleteKey=" + resource.key() + ", type=" + resource.suffix() + ", params=" + params + "]";
325 }
326
327
328
329
330
331 private ResourceContentAnnotator[] getAnnotators(HttpServletRequest request)
332 {
333 if (request == null || !"js".equals(this.getType()) || !"true".equals(request.getParameter("trycatchwrap")))
334 {
335 return DEFAULT_ANNOTATORS;
336 }
337 else
338 {
339 return JS_WRAP_ANNOTATORS;
340 }
341 }
342 }