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.cms.servlets;
35
36 import info.magnolia.cms.util.ClasspathResourcesUtil;
37
38 import java.io.IOException;
39 import java.io.InputStream;
40 import java.net.URL;
41 import java.net.URLConnection;
42 import java.util.Hashtable;
43 import java.util.Map;
44
45 import javax.servlet.ServletException;
46 import javax.servlet.ServletOutputStream;
47 import javax.servlet.http.HttpServlet;
48 import javax.servlet.http.HttpServletRequest;
49 import javax.servlet.http.HttpServletResponse;
50
51 import org.apache.commons.io.IOUtils;
52 import org.apache.commons.lang.StringUtils;
53 import org.slf4j.Logger;
54 import org.slf4j.LoggerFactory;
55
56
57
58
59
60
61
62
63
64
65
66 public class ClasspathSpool extends HttpServlet {
67
68
69
70
71 public static final String MGNL_DEFAULT_RESOURCES_ROOT = "/mgnl-resources";
72
73 private static final long serialVersionUID = 222L;
74
75 private final static Logger log = LoggerFactory.getLogger(ClasspathSpool.class);
76
77 private String resourcesRoot;
78
79 @Override
80 protected long getLastModified(HttpServletRequest req) {
81 String filePath = this.getFilePath(req);
82 try {
83 URL url = ClasspathResourcesUtil.getResource(resourcesRoot + filePath);
84 if(url != null){
85 URLConnection connection = url.openConnection();
86
87 connection.setDoInput(false);
88 connection.setDoOutput(false);
89
90 long lastModified = connection.getLastModified();
91 InputStream is = null;
92 try{
93 is = connection.getInputStream();
94 }
95 finally{
96 IOUtils.closeQuietly(is);
97 }
98 return lastModified;
99 }
100 }
101 catch (IOException e) {
102
103 }
104
105 return -1;
106 }
107
108
109
110
111
112 @Override
113 public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
114
115 String filePath = getFilePath(request);
116
117 if (StringUtils.contains(filePath, "*")) {
118 streamMultipleFile(response, filePath);
119 }
120 else if (StringUtils.contains(filePath, "|")) {
121 String[] paths = StringUtils.split(filePath, "|");
122 streamMultipleFile(response, paths);
123 }
124 else {
125 streamSingleFile(response, filePath);
126 }
127 }
128
129 protected String getFilePath(HttpServletRequest request) {
130
131 String filePath = (String) request.getAttribute("javax.servlet.include.path_info");
132
133
134 if (StringUtils.isEmpty(filePath)) {
135 filePath = (String) request.getAttribute("javax.servlet.forward.path_info");
136 }
137
138
139 if (StringUtils.isEmpty(filePath)) {
140 filePath = request.getPathInfo();
141 }
142 return filePath;
143 }
144
145 private Map<String, String[]> multipleFilePathsCache;
146
147
148
149
150 @Override
151 public void init() throws ServletException {
152 super.init();
153 multipleFilePathsCache = new Hashtable<String, String[]>();
154 resourcesRoot = StringUtils.defaultIfEmpty(getInitParameter("resourcesRoot"), MGNL_DEFAULT_RESOURCES_ROOT);
155
156 URL url = ClasspathResourcesUtil.getResource(resourcesRoot);
157 log.debug("resources root is {}", resourcesRoot);
158 if(url == null) {
159 log.warn("Resource classpath root {} does not seem to exist. Some resources might not be available, please check your configuration. Falling back to default resources root {}", resourcesRoot, MGNL_DEFAULT_RESOURCES_ROOT);
160
161 resourcesRoot = MGNL_DEFAULT_RESOURCES_ROOT;
162 }
163 }
164
165
166
167
168 @Override
169 public void destroy() {
170 super.destroy();
171 multipleFilePathsCache.clear();
172 }
173
174
175
176
177
178 private void streamMultipleFile(HttpServletResponse response, String filePath) throws IOException {
179 log.debug("aggregating files for request {}", filePath);
180
181 String[] paths = multipleFilePathsCache.get(filePath);
182 if (paths == null) {
183 final String startsWith = resourcesRoot + StringUtils.substringBefore(filePath, "*");
184 final String endsWith = StringUtils.substringAfterLast(filePath, "*");
185
186 paths = ClasspathResourcesUtil.findResources(new ClasspathResourcesUtil.Filter() {
187
188 @Override
189 public boolean accept(String name) {
190 return name.startsWith(startsWith) && name.endsWith(endsWith);
191 }
192 });
193 }
194 multipleFilePathsCache.put(filePath, paths);
195
196 if (paths.length == 0) {
197 response.sendError(HttpServletResponse.SC_NOT_FOUND);
198 return;
199 }
200
201 streamMultipleFile(response, paths);
202 }
203
204 private void streamMultipleFile(HttpServletResponse response, String[] paths) throws IOException {
205 ServletOutputStream out = response.getOutputStream();
206 InputStream in = null;
207
208 for (String path : paths) {
209 try {
210 if (!path.startsWith(resourcesRoot)) {
211 path = resourcesRoot + path;
212 }
213 in = ClasspathResourcesUtil.getStream(path);
214 if (in != null) {
215 IOUtils.copy(in, out);
216 }
217 }
218 finally {
219 IOUtils.closeQuietly(in);
220 }
221 }
222
223 out.flush();
224 IOUtils.closeQuietly(out);
225 }
226
227 private void streamSingleFile(HttpServletResponse response, String filePath) throws IOException {
228 InputStream in = null;
229
230
231 try {
232 in = ClasspathResourcesUtil.getStream(resourcesRoot + filePath);
233 }
234 catch (IOException e) {
235 IOUtils.closeQuietly(in);
236 }
237
238 if (in == null) {
239 if (!response.isCommitted()) {
240 response.sendError(HttpServletResponse.SC_NOT_FOUND);
241 }
242 return;
243 }
244
245 try {
246 ServletOutputStream out = response.getOutputStream();
247 IOUtils.copy(in, out);
248 out.flush();
249 IOUtils.closeQuietly(out);
250 }
251 catch (IOException e) {
252
253
254 log.debug("Unable to spool resource due to a {} exception", e.getClass().getName());
255 if (!response.isCommitted()) {
256 response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
257 }
258 }
259 finally {
260 IOUtils.closeQuietly(in);
261 }
262 }
263
264 }