1 package com.atlassian.plugin.servlet;
2
3 import java.io.IOException;
4 import java.io.InputStream;
5 import java.io.OutputStream;
6 import java.util.Date;
7
8 import javax.servlet.http.HttpServletRequest;
9 import javax.servlet.http.HttpServletResponse;
10
11 import com.atlassian.plugin.Plugin;
12 import com.atlassian.plugin.elements.ResourceLocation;
13 import com.atlassian.plugin.servlet.util.LastModifiedHandler;
14 import com.atlassian.plugin.util.PluginUtils;
15
16 import org.apache.commons.io.IOUtils;
17 import org.apache.commons.lang.StringUtils;
18 import org.slf4j.Logger;
19 import org.slf4j.LoggerFactory;
20
21
22
23
24
25
26
27 abstract class AbstractDownloadableResource implements DownloadableResource
28 {
29 private static final Logger log = LoggerFactory.getLogger(AbstractDownloadableResource.class);
30
31
32
33
34
35 private static final String ATLASSIAN_WEBRESOURCE_DISABLE_MINIFICATION = "atlassian.webresource.disable.minification";
36
37
38
39 protected final Plugin plugin;
40 protected final String extraPath;
41 protected final ResourceLocation resourceLocation;
42
43
44
45 private final String location;
46 private final boolean disableMinification;
47
48 public AbstractDownloadableResource(final Plugin plugin, final ResourceLocation resourceLocation, final String extraPath)
49 {
50 this(plugin, resourceLocation, extraPath, false);
51 }
52
53 public AbstractDownloadableResource(final Plugin plugin, final ResourceLocation resourceLocation, String extraPath, final boolean disableMinification)
54 {
55 if ((extraPath != null) && !"".equals(extraPath.trim()) && !resourceLocation.getLocation().endsWith("/"))
56 {
57 extraPath = "/" + extraPath;
58 }
59 this.disableMinification = disableMinification;
60 this.plugin = plugin;
61 this.extraPath = extraPath;
62 this.resourceLocation = resourceLocation;
63 this.location = resourceLocation.getLocation() + extraPath;
64 }
65
66 public void serveResource(final HttpServletRequest request, final HttpServletResponse response) throws DownloadException
67 {
68 log.debug("Serving: {}", this);
69
70 final InputStream resourceStream = getResourceAsStreamViaMinificationStrategy();
71 if (resourceStream == null)
72 {
73 log.warn("Resource not found: {}", this);
74 return;
75 }
76
77 final String contentType = getContentType();
78 if (StringUtils.isNotBlank(contentType))
79 {
80 response.setContentType(contentType);
81 }
82
83 final OutputStream out;
84 try
85 {
86 out = response.getOutputStream();
87 }
88 catch (final IOException e)
89 {
90 throw new DownloadException(e);
91 }
92
93 streamResource(resourceStream, out);
94 log.debug("Serving file done.");
95 }
96
97 public void streamResource(final OutputStream out) throws DownloadException
98 {
99 final InputStream resourceStream = getResourceAsStreamViaMinificationStrategy();
100 if (resourceStream == null)
101 {
102 log.warn("Resource not found: {}", this);
103 return;
104 }
105
106 streamResource(resourceStream, out);
107 }
108
109
110
111
112
113
114
115
116
117
118 private void streamResource(final InputStream in, final OutputStream out) throws DownloadException
119 {
120 try
121 {
122 IOUtils.copy(in, out);
123 }
124 catch (final IOException e)
125 {
126 throw new DownloadException(e);
127 }
128 finally
129 {
130 IOUtils.closeQuietly(in);
131 try
132 {
133 out.flush();
134 }
135 catch (final IOException e)
136 {
137 log.debug("Error flushing output stream", e);
138 }
139 }
140 }
141
142
143
144
145
146
147
148
149
150 public boolean isResourceModified(final HttpServletRequest httpServletRequest, final HttpServletResponse httpServletResponse)
151 {
152 final Date resourceLastModifiedDate = (plugin.getDateLoaded() == null) ? new Date() : plugin.getDateLoaded();
153 final LastModifiedHandler lastModifiedHandler = new LastModifiedHandler(resourceLastModifiedDate);
154 return !lastModifiedHandler.checkRequest(httpServletRequest, httpServletResponse);
155 }
156
157 public String getContentType()
158 {
159 return resourceLocation.getContentType();
160 }
161
162
163
164
165
166
167
168
169
170 protected abstract InputStream getResourceAsStream(String resourceLocation);
171
172
173
174
175
176
177
178 protected String getLocation()
179 {
180 return location;
181 }
182
183 @Override
184 public String toString()
185 {
186 final String pluginKey = plugin != null ? plugin.getKey() : "";
187 return "Resource: " + pluginKey + " " + getLocation() + " (" + getContentType() + ")";
188 }
189
190
191
192
193
194
195
196
197
198 private InputStream getResourceAsStreamViaMinificationStrategy()
199 {
200
201 InputStream inputStream = null;
202 final String location = getLocation();
203 if (minificationStrategyInPlay(location))
204 {
205 final String minifiedLocation = getMinifiedLocation(location);
206 inputStream = getResourceAsStream(minifiedLocation);
207 }
208 if (inputStream == null)
209 {
210 inputStream = getResourceAsStream(location);
211 }
212 return inputStream;
213 }
214
215
216
217
218
219
220
221
222 private boolean minificationStrategyInPlay(final String resourceLocation)
223 {
224
225
226 if (disableMinification)
227 {
228 return false;
229 }
230
231
232
233 try
234 {
235 if (Boolean.getBoolean(ATLASSIAN_WEBRESOURCE_DISABLE_MINIFICATION) || PluginUtils.isAtlassianDevMode())
236 {
237 return false;
238 }
239 }
240 catch (final SecurityException se)
241 {
242
243
244 }
245
246 if (resourceLocation.endsWith(".js"))
247 {
248
249 return !(resourceLocation.endsWith("-min.js") || resourceLocation.endsWith(".min.js"));
250 }
251 if (resourceLocation.endsWith(".css"))
252 {
253
254 return !(resourceLocation.endsWith("-min.css") || resourceLocation.endsWith(".min.css"));
255 }
256
257
258 return false;
259 }
260
261 private String getMinifiedLocation(final String location)
262 {
263 final int lastDot = location.lastIndexOf(".");
264
265
266 return location.substring(0, lastDot) + "-min" + location.substring(lastDot);
267 }
268 }