1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 package info.magnolia.dam.core.download;
35
36 import info.magnolia.cms.filters.SelfMappingServlet;
37 import info.magnolia.dam.api.Asset;
38 import info.magnolia.dam.api.AssetProvider;
39 import info.magnolia.dam.api.AssetProviderRegistry;
40 import info.magnolia.dam.api.AssetProviderRegistry.NoSuchAssetProviderException;
41 import info.magnolia.dam.api.ItemKey;
42 import info.magnolia.dam.api.PathAwareAssetProvider;
43 import info.magnolia.dam.api.PathAwareAssetProvider.PathNotFoundException;
44 import info.magnolia.dam.core.config.DamCoreConfiguration;
45 import info.magnolia.link.LinkUtil;
46
47 import java.io.IOException;
48 import java.io.InputStream;
49 import java.util.ArrayList;
50 import java.util.Enumeration;
51 import java.util.List;
52
53 import javax.inject.Inject;
54 import javax.servlet.ServletException;
55 import javax.servlet.http.HttpServlet;
56 import javax.servlet.http.HttpServletRequest;
57 import javax.servlet.http.HttpServletResponse;
58
59 import org.apache.commons.io.IOUtils;
60 import org.apache.commons.lang3.StringUtils;
61 import org.slf4j.Logger;
62 import org.slf4j.LoggerFactory;
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78 public class DamDownloadServlet extends HttpServlet implements SelfMappingServlet {
79 private static final Logger log = LoggerFactory.getLogger(DamDownloadServlet.class);
80
81 static final String CONTENT_DISPOSITION = "Content-Disposition";
82 static final String LAST_MODIFIED = "Last-Modified";
83
84
85
86
87 public static final String FALL_BACK_PROVIDER_ID = "jcr";
88
89 private final AssetProviderRegistry assetProviderRegistry;
90 private final DamCoreConfiguration configuration;
91
92 @Inject
93 public DamDownloadServlet(final DamCoreConfiguration configuration, final AssetProviderRegistry assetProviderRegistry) {
94 this.configuration = configuration;
95 this.assetProviderRegistry = assetProviderRegistry;
96 }
97
98 @Override
99 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
100 doGet(request, response);
101 }
102
103 @Override
104 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
105 try {
106 process(request, response);
107 } catch (IOException e) {
108 log.error("An IO error occurred during download", e);
109 throw e;
110 } catch (Exception e) {
111 log.error("An error occurred during download", e);
112 throw new ServletException(e);
113 }
114 }
115
116
117
118
119
120
121
122
123 protected void process(HttpServletRequest request, HttpServletResponse response) throws Exception {
124
125 Asset asset = getAsset(request);
126
127 if (asset == null) {
128 response.sendError(HttpServletResponse.SC_NOT_FOUND);
129 return;
130 }
131 handleResourceRequest(request, response, asset);
132 }
133
134 @Override
135 public String getSelfMappingPath() {
136 return configuration.getDownloadPath() + "/*";
137 }
138
139
140
141
142
143
144 protected Asset getAsset(HttpServletRequest request) {
145 Asset asset = null;
146 String pathInfo = request.getPathInfo();
147 try {
148
149 asset = getAssetBasedOnIdentifier(pathInfo);
150
151 if (asset == null) {
152 asset = getAssetBasedOnPath(pathInfo);
153 }
154 } catch (Exception e) {
155 log.warn("Could not retrieve an asset based on the following pathInfo {}", pathInfo, e);
156 }
157 return asset;
158 }
159
160 void handleResourceRequest(HttpServletRequest req, HttpServletResponse res, Asset asset) throws Exception {
161 InputStream is = asset.getContentStream();
162 if (is == null) {
163 res.sendError(HttpServletResponse.SC_NOT_FOUND);
164 return;
165 }
166
167
168 res.setContentType(asset.getMimeType());
169
170
171 if (configuration.getContentDisposition() == null || configuration.getContentDisposition().vote(res) > 0) {
172 res.setHeader(CONTENT_DISPOSITION, "attachment; filename=\"" + asset.getFileName() + "\"");
173 }
174
175
176 if (!configuration.isEnforceDocumentMimeType()) {
177 res.setContentType(null);
178 }
179
180
181 res.setContentLength((int) asset.getFileSize());
182
183 if (asset.getLastModified() != null) {
184 res.setDateHeader(LAST_MODIFIED, asset.getLastModified().getTimeInMillis());
185 }
186
187 try {
188 IOUtils.copyLarge(is, res.getOutputStream());
189 } catch (IOException e) {
190 if (log.isDebugEnabled()) {
191 log.debug(
192 "Download of document [" + asset.getName() + "] with headers " + getHeader(req) + " was interrupted due: " + (e.getMessage() == null ? e.getClass().getName() : e.getMessage())
193 + ":" + (e.getCause() == null ? "" : e.getCause().toString()) + ".", e);
194 }
195
196
197 if (res.getStatus() != HttpServletResponse.SC_PARTIAL_CONTENT) {
198 throw e;
199 }
200 } finally {
201 IOUtils.closeQuietly(is);
202 }
203 }
204
205 private List<String> getHeader(HttpServletRequest req) {
206 List<String> out = new ArrayList<String>();
207 @SuppressWarnings("unchecked")
208 Enumeration<String> names = req.getHeaderNames();
209 while (names.hasMoreElements()) {
210 String name = names.nextElement();
211 out.add(name + "=" + req.getHeader(name));
212 }
213 return out;
214 }
215
216
217
218
219
220
221
222 private Asset getAssetBasedOnIdentifier(String pathInfo) {
223
224 String keyStr = pathInfo.split("/")[1];
225
226 if (ItemKey.isValid(keyStr)) {
227 final ItemKey itemKey = ItemKey.from(keyStr);
228 return assetProviderRegistry.getProviderFor(itemKey).getAsset(itemKey);
229 }
230 return null;
231 }
232
233
234
235
236
237
238
239
240 private Asset getAssetBasedOnPath(String pathInfo) {
241 AssetProvider assetProvider = null;
242
243 String keyStr = pathInfo.split("/")[1];
244 String path = pathInfo;
245
246 try {
247
248 assetProvider = assetProviderRegistry.getProviderById(keyStr);
249 path = path.replaceFirst("/" + assetProvider.getIdentifier() + "/", "/");
250 } catch (NoSuchAssetProviderException nsape) {
251
252 assetProvider = assetProviderRegistry.getProviderById(FALL_BACK_PROVIDER_ID);
253 }
254
255 if (assetProvider instanceof PathAwareAssetProvider) {
256
257
258 String extension = StringUtils.substringAfterLast(path, ".");
259 String assetPath = LinkUtil.removeFingerprintAndExtensionFromLink(path);
260
261 try {
262 return ((PathAwareAssetProvider) assetProvider).getAsset(assetPath);
263 } catch (PathNotFoundException e) {
264
265 }
266
267 try {
268 return ((PathAwareAssetProvider) assetProvider).getAsset(assetPath + "." + extension);
269 } catch (PathNotFoundException e) {
270 log.warn("No asset could be found for the following path {}", assetPath);
271 }
272 }
273 log.warn("Provider {} can not support assets search based on path", assetProvider.getIdentifier());
274 return null;
275 }
276 }