1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.myfaces.trinidad.webapp;
20
21 import java.io.BufferedReader;
22 import java.io.File;
23 import java.io.IOException;
24 import java.io.InputStream;
25 import java.io.InputStreamReader;
26 import java.io.InterruptedIOException;
27 import java.io.OutputStream;
28 import java.io.Reader;
29 import java.lang.reflect.Constructor;
30 import java.lang.reflect.InvocationTargetException;
31 import java.net.SocketException;
32 import java.net.UnknownServiceException;
33 import java.net.URL;
34 import java.net.URLConnection;
35 import java.util.HashMap;
36 import java.util.Map;
37
38 import javax.faces.FacesException;
39 import javax.faces.FactoryFinder;
40 import javax.faces.context.FacesContext;
41 import javax.faces.context.FacesContextFactory;
42 import javax.faces.event.PhaseListener;
43 import javax.faces.lifecycle.Lifecycle;
44 import javax.servlet.ServletConfig;
45 import javax.servlet.ServletContext;
46 import javax.servlet.ServletException;
47 import javax.servlet.ServletRequest;
48 import javax.servlet.ServletResponse;
49 import javax.servlet.http.HttpServlet;
50 import javax.servlet.http.HttpServletRequest;
51 import javax.servlet.http.HttpServletResponse;
52
53 import org.apache.myfaces.trinidad.config.Configurator;
54 import org.apache.myfaces.trinidad.logging.TrinidadLogger;
55 import org.apache.myfaces.trinidad.resource.CachingResourceLoader;
56 import org.apache.myfaces.trinidad.resource.DirectoryResourceLoader;
57 import org.apache.myfaces.trinidad.resource.ResourceLoader;
58 import org.apache.myfaces.trinidad.resource.ServletContextResourceLoader;
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78 public class ResourceServlet extends HttpServlet
79 {
80
81
82
83 private static final long serialVersionUID = 4547362994406585148L;
84
85
86
87
88 @Override
89 public void destroy()
90 {
91 _loaders = null;
92 _facesContextFactory = null;
93 _lifecycle = null;
94
95 super.destroy();
96 }
97
98
99
100
101 @Override
102 public void init(
103 ServletConfig config
104 ) throws ServletException
105 {
106 super.init(config);
107
108
109 try
110 {
111 _facesContextFactory = (FacesContextFactory)
112 FactoryFinder.getFactory
113 (FactoryFinder.FACES_CONTEXT_FACTORY);
114 }
115 catch (FacesException e)
116 {
117 Throwable rootCause = e.getCause();
118 if (rootCause == null)
119 {
120 throw e;
121 }
122 else
123 {
124 throw new ServletException(e.getMessage(), rootCause);
125 }
126 }
127
128
129 _lifecycle = new _ResourceLifecycle();
130 _initDebug(config);
131 _loaders = new HashMap<String, ResourceLoader>();
132 }
133
134 @Override
135 public void service(
136 ServletRequest request,
137 ServletResponse response
138 ) throws ServletException, IOException
139 {
140 boolean hasFacesContext = false;
141 FacesContext context = FacesContext.getCurrentInstance();
142
143
144
145
146 if (context != null)
147 {
148 hasFacesContext = true;
149 }
150 else
151 {
152 Configurator.disableConfiguratorServices(request);
153
154
155
156
157 context = _facesContextFactory.getFacesContext(getServletContext(), request, response, _lifecycle);
158 }
159
160 try
161 {
162 super.service(request, response);
163 }
164 catch (ServletException e)
165 {
166 _LOG.severe(e);
167 throw e;
168 }
169 catch (IOException e)
170 {
171 if (!_canIgnore(e))
172 _LOG.severe(e);
173 throw e;
174 }
175 finally
176 {
177 if (!hasFacesContext)
178 context.release();
179 }
180 }
181
182
183
184
185 @Override
186 protected void doGet(
187 HttpServletRequest request,
188 HttpServletResponse response
189 ) throws ServletException, IOException
190 {
191 ResourceLoader loader = _getResourceLoader(request);
192 String resourcePath = getResourcePath(request);
193 URL url = loader.getResource(resourcePath);
194
195
196 if (url == null)
197 {
198 response.sendError(HttpServletResponse.SC_NOT_FOUND);
199 return;
200 }
201
202
203 URLConnection connection = url.openConnection();
204 connection.setDoInput(true);
205 connection.setDoOutput(false);
206
207 _setHeaders(connection, response);
208
209 InputStream in = connection.getInputStream();
210 OutputStream out = response.getOutputStream();
211 byte[] buffer = new byte[_BUFFER_SIZE];
212
213 try
214 {
215 _pipeBytes(in, out, buffer);
216 }
217 finally
218 {
219 try
220 {
221 in.close();
222 }
223 finally
224 {
225 out.close();
226 }
227 }
228 }
229
230
231
232
233 @Override
234 protected long getLastModified(
235 HttpServletRequest request)
236 {
237 try
238 {
239 ResourceLoader loader = _getResourceLoader(request);
240 String resourcePath = getResourcePath(request);
241 URL url = loader.getResource(resourcePath);
242
243 if (url == null)
244 return super.getLastModified(request);
245
246 URLConnection connection = url.openConnection();
247 connection.setDoInput(false);
248 connection.setDoOutput(false);
249
250 long lastModified = connection.getLastModified();
251
252 try
253 {
254 InputStream is = connection.getInputStream();
255 if (is != null)
256 is.close();
257 }
258 catch (UnknownServiceException use)
259 {
260 }
261
262 return lastModified;
263 }
264 catch (IOException e)
265 {
266
267
268 return super.getLastModified(request);
269 }
270 }
271
272
273
274
275
276
277
278
279 protected String getResourcePath(
280 HttpServletRequest request)
281 {
282 return request.getServletPath() + request.getPathInfo();
283 }
284
285
286
287
288 private ResourceLoader _getResourceLoader(
289 HttpServletRequest request)
290 {
291 final String servletPath = request.getServletPath();
292 ResourceLoader loader = _loaders.get(servletPath);
293
294 if (loader == null)
295 {
296 try
297 {
298 String key = "META-INF/servlets/resources" +
299 servletPath +
300 ".resources";
301 ClassLoader cl = Thread.currentThread().getContextClassLoader();
302 URL url = cl.getResource(key);
303
304 if (url != null)
305 {
306 Reader r = new InputStreamReader(url.openStream());
307 BufferedReader br = new BufferedReader(r);
308 try
309 {
310 String className = br.readLine();
311 if (className != null)
312 {
313 className = className.trim();
314 Class<?> clazz = cl.loadClass(className);
315 try
316 {
317 Constructor<?> decorator = clazz.getConstructor(_DECORATOR_SIGNATURE);
318 ServletContext context = getServletContext();
319 File tempdir = (File)
320 context.getAttribute("javax.servlet.context.tempdir");
321 ResourceLoader delegate = new DirectoryResourceLoader(tempdir);
322 loader = (ResourceLoader)
323 decorator.newInstance(new Object[]{delegate});
324 }
325 catch (InvocationTargetException e)
326 {
327
328 loader = (ResourceLoader) clazz.newInstance();
329 }
330 catch (NoSuchMethodException e)
331 {
332
333 loader = (ResourceLoader) clazz.newInstance();
334 }
335 }
336 }
337 finally
338 {
339 br.close();
340 }
341 }
342 else
343 {
344
345 _LOG.warning("Unable to find ResourceLoader for ResourceServlet" +
346 " at servlet path:{0}" +
347 "\nCause: Could not find resource:{1}",
348 new Object[] {servletPath, key});
349 loader = new ServletContextResourceLoader(getServletContext())
350 {
351 @Override
352 public URL getResource(
353 String path) throws IOException
354 {
355 return super.getResource(path);
356 }
357 };
358 }
359
360
361 if (!_debug)
362 loader = new CachingResourceLoader(loader);
363 }
364 catch (IllegalAccessException e)
365 {
366 loader = ResourceLoader.getNullResourceLoader();
367 }
368 catch (InstantiationException e)
369 {
370 loader = ResourceLoader.getNullResourceLoader();
371 }
372 catch (ClassNotFoundException e)
373 {
374 loader = ResourceLoader.getNullResourceLoader();
375 }
376 catch (IOException e)
377 {
378 loader = ResourceLoader.getNullResourceLoader();
379 }
380
381 _loaders.put(servletPath, loader);
382 }
383
384 return loader;
385 }
386
387
388
389
390
391 private static void _pipeBytes(
392 InputStream in,
393 OutputStream out,
394 byte[] buffer
395 ) throws IOException
396 {
397 int length;
398
399 while ((length = (in.read(buffer))) >= 0)
400 {
401 out.write(buffer, 0, length);
402 }
403 }
404
405
406
407
408 private void _initDebug(
409 ServletConfig config
410 )
411 {
412 String debug = config.getInitParameter(DEBUG_INIT_PARAM);
413 if (debug == null)
414 {
415
416
417 debug = config.getServletContext().getInitParameter(DEBUG_INIT_PARAM);
418 }
419
420 _debug = "true".equalsIgnoreCase(debug);
421 if (_debug)
422 {
423 _LOG.info("RESOURCESERVLET_IN_DEBUG_MODE",DEBUG_INIT_PARAM);
424 }
425 }
426
427
428
429
430
431 private void _setHeaders(
432 URLConnection connection,
433 HttpServletResponse response)
434 {
435 String contentType = connection.getContentType();
436 if (contentType == null || "content/unknown".equals(contentType))
437 {
438 URL url = connection.getURL();
439 String resourcePath = url.getPath();
440 if(resourcePath.endsWith(".css"))
441 contentType = "text/css";
442 else
443 contentType = getServletContext().getMimeType(resourcePath);
444 }
445 response.setContentType(contentType);
446
447 int contentLength = connection.getContentLength();
448 if (contentLength >= 0)
449 response.setContentLength(contentLength);
450
451 long lastModified = connection.getLastModified();
452 if (lastModified >= 0)
453 response.setDateHeader("Last-Modified", lastModified);
454
455
456 if (!_debug)
457 {
458
459
460
461
462
463 response.setHeader("Cache-Control", "Public");
464
465
466 long currentTime = System.currentTimeMillis();
467
468 response.setDateHeader("Expires", currentTime + ONE_YEAR_MILLIS);
469 }
470 }
471
472 private static boolean _canIgnore(Throwable t)
473 {
474 if (t instanceof InterruptedIOException)
475 {
476
477 return true;
478 }
479 else if (t instanceof SocketException)
480 {
481
482
483
484
485 return true;
486 }
487 else if (t instanceof IOException)
488 {
489 String message = t.getMessage();
490
491
492 if ((message != null) &&
493 ((message.indexOf("Broken pipe") >= 0) ||
494 (message.indexOf("abort") >= 0)))
495 return true;
496 }
497 return false;
498 }
499
500 static private class _ResourceLifecycle extends Lifecycle
501 {
502 @Override
503 public void execute(FacesContext p0) throws FacesException
504 {
505 }
506
507 @Override
508 public PhaseListener[] getPhaseListeners()
509 {
510 return null;
511 }
512
513 @Override
514 public void removePhaseListener(PhaseListener p0)
515 {
516 }
517
518 @Override
519 public void render(FacesContext p0) throws FacesException
520 {
521 }
522
523 @Override
524 public void addPhaseListener(PhaseListener p0)
525 {
526 }
527 }
528
529
530
531
532
533 public static final String DEBUG_INIT_PARAM =
534 "org.apache.myfaces.trinidad.resource.DEBUG";
535
536
537
538
539 public static final long ONE_YEAR_MILLIS = 31363200000L;
540
541
542 private static final Class[] _DECORATOR_SIGNATURE =
543 new Class[]{ResourceLoader.class};
544
545 private static final TrinidadLogger _LOG = TrinidadLogger.createTrinidadLogger(ResourceServlet.class);
546
547
548 private static final int _BUFFER_SIZE = 2048;
549
550 private boolean _debug;
551 private Map<String, ResourceLoader> _loaders;
552 private FacesContextFactory _facesContextFactory;
553 private Lifecycle _lifecycle;
554 }