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 org.apache.commons.io.IOUtils;
12 import org.apache.commons.lang.StringUtils;
13 import org.slf4j.Logger;
14 import org.slf4j.LoggerFactory;
15
16 import com.atlassian.plugin.Plugin;
17 import com.atlassian.plugin.elements.ResourceLocation;
18 import com.atlassian.plugin.servlet.util.LastModifiedHandler;
19 import com.atlassian.plugin.util.PluginUtils;
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 if (log.isDebugEnabled())
69 {
70 log.debug("Serving: " + this);
71 }
72
73 final InputStream resourceStream = getResourceAsStreamViaMinificationStrategy();
74 if (resourceStream == null)
75 {
76 log.warn("Resource not found: " + this);
77 return;
78 }
79
80 final String contentType = getContentType();
81 if (StringUtils.isNotBlank(contentType))
82 {
83 response.setContentType(contentType);
84 }
85
86 OutputStream out;
87 try
88 {
89 out = response.getOutputStream();
90 }
91 catch (final IOException e)
92 {
93 throw new DownloadException(e);
94 }
95
96 streamResource(resourceStream, out);
97 log.debug("Serving file done.");
98 }
99
100 public void streamResource(final OutputStream out) throws DownloadException
101 {
102 final InputStream resourceStream = getResourceAsStreamViaMinificationStrategy();
103 if (resourceStream == null)
104 {
105 log.warn("Resource not found: " + this);
106 return;
107 }
108
109 streamResource(resourceStream, out);
110 }
111
112
113
114
115
116
117
118
119
120
121 private void streamResource(final InputStream in, final OutputStream out) throws DownloadException
122 {
123 try
124 {
125 IOUtils.copy(in, out);
126 }
127 catch (final IOException e)
128 {
129 throw new DownloadException(e);
130 }
131 finally
132 {
133 IOUtils.closeQuietly(in);
134 try
135 {
136 out.flush();
137 }
138 catch (final IOException e)
139 {
140 log.debug("Error flushing output stream", e);
141 }
142 }
143 }
144
145
146
147
148
149
150
151
152
153 public boolean isResourceModified(final HttpServletRequest httpServletRequest, final HttpServletResponse httpServletResponse)
154 {
155 final Date resourceLastModifiedDate = (plugin.getDateLoaded() == null) ? new Date() : plugin.getDateLoaded();
156 final LastModifiedHandler lastModifiedHandler = new LastModifiedHandler(resourceLastModifiedDate);
157 return lastModifiedHandler.checkRequest(httpServletRequest, httpServletResponse);
158 }
159
160 public String getContentType()
161 {
162 return resourceLocation.getContentType();
163 }
164
165
166
167
168
169
170
171
172
173 protected abstract InputStream getResourceAsStream(String resourceLocation);
174
175
176
177
178
179
180
181 protected String getLocation()
182 {
183 return location;
184 }
185
186 @Override
187 public String toString()
188 {
189 return "Resource: " + getLocation() + " (" + getContentType() + ")";
190 }
191
192
193
194
195
196
197
198
199
200 private InputStream getResourceAsStreamViaMinificationStrategy()
201 {
202
203 InputStream inputStream = null;
204 final String location = getLocation();
205 if (minificationStrategyInPlay(location))
206 {
207 final String minifiedLocation = getMinifiedLocation(location);
208 inputStream = getResourceAsStream(minifiedLocation);
209 }
210 if (inputStream == null)
211 {
212 inputStream = getResourceAsStream(location);
213 }
214 return inputStream;
215 }
216
217
218
219
220
221
222
223
224 private boolean minificationStrategyInPlay(final String resourceLocation)
225 {
226
227
228 if (disableMinification)
229 {
230 return false;
231 }
232
233
234
235 try
236 {
237 if (Boolean.getBoolean(ATLASSIAN_WEBRESOURCE_DISABLE_MINIFICATION) || Boolean.getBoolean(PluginUtils.ATLASSIAN_DEV_MODE))
238 {
239 return false;
240 }
241 }
242 catch (final SecurityException se)
243 {
244
245
246 }
247
248 if (resourceLocation.endsWith(".js"))
249 {
250
251 return !(resourceLocation.endsWith("-min.js") || resourceLocation.endsWith(".min.js"));
252 }
253 if (resourceLocation.endsWith(".css"))
254 {
255
256 return !(resourceLocation.endsWith("-min.css") || resourceLocation.endsWith(".min.css"));
257 }
258
259
260 return false;
261 }
262
263 private String getMinifiedLocation(final String location)
264 {
265 final int lastDot = location.lastIndexOf(".");
266
267
268 return location.substring(0, lastDot) + "-min" + location.substring(lastDot);
269 }
270 }