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