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 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 return "Resource: " + plugin.getKey() + " " + getLocation() + " (" + getContentType() + ")";
187 }
188
189
190
191
192
193
194
195
196
197 private InputStream getResourceAsStreamViaMinificationStrategy()
198 {
199
200 InputStream inputStream = null;
201 final String location = getLocation();
202 if (minificationStrategyInPlay(location))
203 {
204 final String minifiedLocation = getMinifiedLocation(location);
205 inputStream = getResourceAsStream(minifiedLocation);
206 }
207 if (inputStream == null)
208 {
209 inputStream = getResourceAsStream(location);
210 }
211 return inputStream;
212 }
213
214
215
216
217
218
219
220
221 private boolean minificationStrategyInPlay(final String resourceLocation)
222 {
223
224
225 if (disableMinification)
226 {
227 return false;
228 }
229
230
231
232 try
233 {
234 if (Boolean.getBoolean(ATLASSIAN_WEBRESOURCE_DISABLE_MINIFICATION) || Boolean.getBoolean(PluginUtils.ATLASSIAN_DEV_MODE))
235 {
236 return false;
237 }
238 }
239 catch (final SecurityException se)
240 {
241
242
243 }
244
245 if (resourceLocation.endsWith(".js"))
246 {
247
248 return !(resourceLocation.endsWith("-min.js") || resourceLocation.endsWith(".min.js"));
249 }
250 if (resourceLocation.endsWith(".css"))
251 {
252
253 return !(resourceLocation.endsWith("-min.css") || resourceLocation.endsWith(".min.css"));
254 }
255
256
257 return false;
258 }
259
260 private String getMinifiedLocation(final String location)
261 {
262 final int lastDot = location.lastIndexOf(".");
263
264
265 return location.substring(0, lastDot) + "-min" + location.substring(lastDot);
266 }
267 }