001: /*
002: * This file is part of the WfMOpen project.
003: * Copyright (C) 2001-2005 Danet GmbH (www.danet.de), BU BTS.
004: * All rights reserved.
005: *
006: * This program is free software; you can redistribute it and/or modify
007: * it under the terms of the GNU General Public License as published by
008: * the Free Software Foundation; either version 2 of the License, or
009: * (at your option) any later version.
010: *
011: * This program is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014: * GNU General Public License for more details.
015: *
016: * You should have received a copy of the GNU General Public License
017: * along with this program; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
019: *
020: * $Id: PortletAppAddResource.java,v 1.12 2007/03/27 21:59:44 mlipp Exp $
021: *
022: * $Log: PortletAppAddResource.java,v $
023: * Revision 1.12 2007/03/27 21:59:44 mlipp
024: * Fixed lots of checkstyle warnings.
025: *
026: * Revision 1.11 2007/01/18 15:07:11 drmlipp
027: * Made JavaScript loading synchronous in IE.
028: *
029: * Revision 1.10 2007/01/17 12:52:41 drmlipp
030: * Fixed problem with "defer" always being set.
031: *
032: * Revision 1.9 2006/11/01 16:18:51 mlipp
033: * Make our bridge depend on core only.
034: *
035: * Revision 1.8 2006/10/06 15:26:51 drmlipp
036: * Improved.
037: *
038: * Revision 1.7 2006/09/29 12:32:11 drmlipp
039: * Consistently using WfMOpen as projct name now.
040: *
041: * Revision 1.6 2006/09/14 13:33:46 drmlipp
042: * Removed superfluous code.
043: *
044: * Revision 1.5 2006/09/12 12:55:39 drmlipp
045: * Added event handler registration and did some cleanup.
046: *
047: * Revision 1.4 2006/09/12 05:56:26 mlipp
048: * Fixed attribute name.
049: *
050: * Revision 1.3 2006/09/11 15:29:28 drmlipp
051: * Improved portlet support.
052: *
053: * Revision 1.2 2006/09/11 10:50:22 drmlipp
054: * Portal specific implementation.
055: *
056: * Revision 1.1 2006/09/08 14:08:06 drmlipp
057: * Started handling of MyFaces resource loading.
058: *
059: */
060: package de.danet.an.util.jsf;
061:
062: import java.io.IOException;
063: import java.util.Map;
064:
065: import javax.faces.FacesException;
066: import javax.faces.context.FacesContext;
067: import javax.faces.context.ResponseWriter;
068: import javax.portlet.RenderRequest;
069: import javax.portlet.RenderResponse;
070: import javax.servlet.ServletContext;
071: import javax.servlet.http.HttpServletRequest;
072: import javax.servlet.http.HttpServletResponse;
073:
074: import org.apache.myfaces.renderkit.html.util.AddResource;
075: import org.apache.myfaces.renderkit.html.util.MyFacesResourceHandler;
076: import org.apache.myfaces.renderkit.html.util.ResourceHandler;
077: import org.apache.myfaces.renderkit.html.util.ResourceLoader;
078: import org.apache.myfaces.renderkit.html.util.ResourcePosition;
079: import org.apache.myfaces.shared_impl.renderkit.html.HTML;
080: import org.apache.myfaces.shared_impl.util.ClassUtils;
081:
082: /**
083: * This class provides an adapted <code>AddResource</code> implementation.
084: *
085: * @author Michael Lipp
086: *
087: */
088: public class PortletAppAddResource implements AddResource {
089:
090: private static final org.apache.commons.logging.Log logger = org.apache.commons.logging.LogFactory
091: .getLog(PortletAppAddResource.class);
092:
093: private static final String PATH_SEPARATOR = "/";
094: private static final String RESOURCE_VIRTUAL_PATH = "/faces/myFacesExtensionResource";
095: private static final String COND_ADD_FUNCTIONS_ADDED = PortletAppAddResource.class
096: .getName()
097: + "_COND_ADD_FUNCTIONS_ADDED";
098: private static final String RESOURCES_CACHE_KEY = PortletAppAddResource.class
099: .getName()
100: + ".CACHE_KEY";
101:
102: private String contextPath = null;
103:
104: /**
105: * Return a value used in the {cacheKey} part of a generated URL for a
106: * resource reference.
107: * <p/>
108: * Caching in browsers normally works by having files served to them
109: * include last-modified and expiry-time http headers. Until the expiry
110: * time is reached, a browser will silently use its cached version. After
111: * the expiry time, it will send a "get if modified since {time}" message,
112: * where {time} is the last-modified header from the version it has cached.
113: * <p/>
114: * Unfortunately this scheme only works well for resources represented as
115: * plain files on disk, where the webserver can easily and efficiently see
116: * the last-modified time of the resource file. When that query has to be
117: * processed by a servlet that doesn't scale well, even when it is possible
118: * to determine the resource's last-modified date from servlet code.
119: * <p/>
120: * Fortunately, for the AddResource class a static resource is only ever
121: * accessed because a URL was embedded by this class in a dynamic page.
122: * This makes it possible to implement caching by instead marking every
123: * resource served with a very long expiry time, but forcing the URL that
124: * points to the resource to change whenever the old cached version becomes
125: * invalid; the browser effectively thinks it is fetching a different
126: * resource that it hasn't seen before. This is implemented by embedding
127: * a "cache key" in the generated URL.
128: * <p/>
129: * Rather than using the actual modification date of a resource as the
130: * cache key, we simply use the webapp deployment time. This means that all
131: * data cached by browsers will become invalid after a webapp deploy (all
132: * the urls to the resources change). It also means that changes that occur
133: * to a resource <i>without</i> a webapp redeploy will not be seen by browsers.
134: */
135: protected long getCacheKey(FacesContext context) {
136: // cache key is hold in application scope so it is recreated on
137: // redeploying the webapp.
138: Map applicationMap = context.getExternalContext()
139: .getApplicationMap();
140: Long cacheKey = (Long) applicationMap.get(RESOURCES_CACHE_KEY);
141: if (cacheKey == null) {
142: cacheKey = new Long(System.currentTimeMillis() / 100000);
143: applicationMap.put(RESOURCES_CACHE_KEY, cacheKey);
144: }
145: return cacheKey.longValue();
146: }
147:
148: /**
149: * Insert a [script src="url"] entry at the current location in the
150: * response. The resource is expected to be in the classpath, at the same
151: * location as the specified component + "/resource". <p/> Example: when
152: * customComponent is class example.Widget, and resourceName is script.js,
153: * the resource will be retrieved from "example/Widget/resource/script.js"
154: * in the classpath.
155: */
156: public void addJavaScriptHere(FacesContext context,
157: Class myfacesCustomComponent, String resourceName)
158: throws IOException {
159: addJavaScriptHere(context, new MyFacesResourceHandler(
160: myfacesCustomComponent, resourceName));
161: }
162:
163: /**
164: * Insert a [script src="url"] entry at the current location in the
165: * response.
166: *
167: * @param uri
168: * is the location of the desired resource, relative to the base
169: * directory of the webapp (ie its contextPath).
170: */
171: public void addJavaScriptHere(FacesContext context, String uri)
172: throws IOException {
173: doAddJavaScriptHere(context, getResourceUri(context, uri));
174: }
175:
176: public void addJavaScriptHerePlain(FacesContext context, String uri)
177: throws IOException {
178: addJavaScriptHere(context, uri);
179: }
180:
181: /**
182: * Insert a [script src="url"] entry at the current location in the
183: * response.
184: *
185: * @param context
186: * @param resourceHandler
187: * is an object which specifies exactly how to build the url that
188: * is emitted into the script tag. Code which needs to generate
189: * URLs in ways that this class does not support by default can
190: * implement a custom ResourceHandler.
191: * @throws IOException
192: */
193: public void addJavaScriptHere(FacesContext context,
194: ResourceHandler resourceHandler) throws IOException {
195: validateResourceHandler(resourceHandler);
196: doAddJavaScriptHere(context, getResourceUri(context,
197: resourceHandler));
198: }
199:
200: private void doAddJavaScriptHere(FacesContext context,
201: String resourceUrl) throws IOException {
202: ResponseWriter writer = context.getResponseWriter();
203: writer.startElement(HTML.SCRIPT_ELEM, null);
204: writer.writeAttribute(HTML.SCRIPT_TYPE_ATTR,
205: HTML.SCRIPT_TYPE_TEXT_JAVASCRIPT, null);
206: String src = context.getExternalContext().encodeResourceURL(
207: resourceUrl);
208: writer.writeURIAttribute(HTML.SRC_ATTR, src, null);
209: writer.endElement(HTML.SCRIPT_ELEM);
210: }
211:
212: /**
213: * Adds the given Javascript resource to the document header at the
214: * specified document positioy by supplying a resourcehandler instance. <p/>
215: * Use this method to have full control about building the reference url to
216: * identify the resource and to customize how the resource is written to the
217: * response. In most cases, however, one of the convenience methods on this
218: * class can be used without requiring a custom ResourceHandler to be
219: * provided. <p/>
220: * If the script has already been referenced, it's added only
221: * once. <p/>
222: * Note that this method <i>queues</i> the javascript for
223: * insertion, and that the script is inserted into the buffered response by
224: * the ExtensionsFilter after the page is complete.
225: */
226: public void addJavaScriptAtPosition(FacesContext context,
227: ResourcePosition position, ResourceHandler resourceHandler) {
228: addJavaScriptAtPosition(context, position, resourceHandler,
229: false);
230: }
231:
232: /**
233: * Insert a [script src="url"] entry into the document header at the
234: * specified document position. If the script has already been referenced,
235: * it's added only once. <p/>
236: * The resource is expected to be in the
237: * classpath, at the same location as the specified component + "/resource".
238: * <p/>
239: * Example: when customComponent is class example.Widget, and
240: * resourceName is script.js, the resource will be retrieved from
241: * "example/Widget/resource/script.js" in the classpath.
242: */
243: public void addJavaScriptAtPosition(FacesContext context,
244: ResourcePosition position, Class myfacesCustomComponent,
245: String resourceName) {
246: addJavaScriptAtPosition(context, position,
247: new MyFacesResourceHandler(myfacesCustomComponent,
248: resourceName));
249: }
250:
251: public void addJavaScriptAtPositionPlain(FacesContext context,
252: ResourcePosition position, Class myfacesCustomComponent,
253: String resourceName) {
254: addJavaScriptAtPosition(context, position,
255: new MyFacesResourceHandler(myfacesCustomComponent,
256: resourceName), false);
257: }
258:
259: /**
260: * Insert a [script src="url"] entry into the document header at the
261: * specified document position. If the script has already been referenced,
262: * it's added only once.
263: *
264: * @param defer
265: * specifies whether the html attribute "defer" is set on the
266: * generated script tag. If this is true then the browser will
267: * continue processing the html page without waiting for the
268: * specified script to load and be run.
269: */
270: public void addJavaScriptAtPosition(FacesContext context,
271: ResourcePosition position, Class myfacesCustomComponent,
272: String resourceName, boolean defer) {
273: addJavaScriptAtPosition(context, position,
274: new MyFacesResourceHandler(myfacesCustomComponent,
275: resourceName), defer);
276: }
277:
278: /**
279: * Insert a [script src="url"] entry into the document header at the
280: * specified document position. If the script has already been referenced,
281: * it's added only once.
282: *
283: * @param uri
284: * is the location of the desired resource, relative to the base
285: * directory of the webapp (ie its contextPath).
286: */
287: public void addJavaScriptAtPosition(FacesContext context,
288: ResourcePosition position, String uri) {
289: addJavaScriptAtPosition(context, position, uri, false);
290: }
291:
292: /**
293: * Adds the given Javascript resource at the specified document position. If
294: * the script has already been referenced, it's added only once.
295: */
296: public void addJavaScriptAtPosition(FacesContext context,
297: ResourcePosition position, String uri, boolean defer) {
298: doAddJavaScriptAtPosition(context, position, getResourceUri(
299: context, uri), defer);
300: }
301:
302: public void addJavaScriptToBodyTag(FacesContext context,
303: String javascriptEventName, String addedJavaScript) {
304: try {
305: ResponseWriter writer = context.getResponseWriter();
306: writer.startElement(HTML.SCRIPT_ELEM, null);
307: writer.writeAttribute(HTML.SCRIPT_TYPE_ATTR,
308: HTML.SCRIPT_TYPE_TEXT_JAVASCRIPT, null);
309: writer.write("de_danet_an_util_jsf_addBodyHandler(\""
310: + javascriptEventName + "\", " + addedJavaScript
311: + "\");\n");
312: writer.endElement(HTML.SCRIPT_ELEM);
313: } catch (IOException e) {
314: throw (IllegalStateException) (new IllegalStateException(e
315: .getMessage())).initCause(e);
316: }
317: }
318:
319: /**
320: * Adds the given Javascript resource at the specified document position. If
321: * the script has already been referenced, it's added only once.
322: */
323: public void addJavaScriptAtPosition(FacesContext context,
324: ResourcePosition position, ResourceHandler resourceHandler,
325: boolean defer) {
326: validateResourceHandler(resourceHandler);
327: doAddJavaScriptAtPosition(context, position, getResourceUri(
328: context, resourceHandler), defer);
329: }
330:
331: private void doAddJavaScriptAtPosition(FacesContext context,
332: ResourcePosition position, String uri, boolean defer) {
333:
334: addCondAddFunctions(context);
335: try {
336: uri = context.getExternalContext().encodeResourceURL(uri);
337: ResponseWriter writer = context.getResponseWriter();
338: writer.startElement(HTML.SCRIPT_ELEM, null);
339: writer.writeAttribute(HTML.SCRIPT_TYPE_ATTR,
340: HTML.SCRIPT_TYPE_TEXT_JAVASCRIPT, null);
341: writer.write("de_danet_an_util_jsf_condAddScript(\"" + uri
342: + "\", " + Boolean.toString(defer).toLowerCase()
343: + ");\n");
344: writer.endElement(HTML.SCRIPT_ELEM);
345: } catch (IOException e) {
346: throw (IllegalStateException) (new IllegalStateException(e
347: .getMessage())).initCause(e);
348: }
349: }
350:
351: /**
352: * @param context
353: */
354: private void addCondAddFunctions(FacesContext context) {
355: if (context.getExternalContext().getRequestMap().containsKey(
356: COND_ADD_FUNCTIONS_ADDED)) {
357: return;
358: }
359: try {
360: ResponseWriter writer = context.getResponseWriter();
361: // Dummy for function ...addBodyHandler below
362: writer.startElement(HTML.DIV_ELEM, null);
363: writer.writeAttribute(HTML.ID_ATTR,
364: "de_danet_an_util_jsf_eventTypeDummy", null);
365: writer.writeAttribute(HTML.STYLE_ATTR, "visibility:hidden",
366: null);
367: writer.writeAttribute(HTML.ONCLICK_ATTR,
368: "alert(\"Don't!\")", null);
369: writer.endElement(HTML.DIV_ELEM);
370: writer.startElement(HTML.SCRIPT_ELEM, null);
371: writer.writeAttribute(HTML.SCRIPT_TYPE_ATTR,
372: HTML.SCRIPT_TYPE_TEXT_JAVASCRIPT, null);
373: // Adding JavaScript
374: writer
375: .write("function de_danet_an_util_jsf_condAddScript (uri, defer) {\n");
376: writer
377: .write(" if (window.de_danet_an_util_jsf_condAddScript.loaded == undefined) {\n");
378: writer
379: .write(" window.de_danet_an_util_jsf_condAddScript.loaded = {};\n");
380: writer.write(" }\n");
381: writer
382: .write(" if (window.de_danet_an_util_jsf_condAddScript.loaded[uri]) {\n");
383: writer.write(" return;\n");
384: writer.write(" }\n");
385: writer
386: .write(" if (navigator.appName == \"Microsoft Internet Explorer\") {\n");
387: writer.write(" var ajaxRequest = null;\n");
388: writer.write(" try {\n");
389: writer
390: .write(" ajaxRequest = new ActiveXObject(\"Msxml2.XMLHTTP\");\n");
391: writer.write(" } catch (e) {\n");
392: writer.write(" try {\n");
393: writer
394: .write(" ajaxRequest = new ActiveXObject(\"Microsoft.XMLHTTP\");\n");
395: writer.write(" } catch (e) {\n");
396: writer.write(" }\n");
397: writer.write(" }\n");
398: writer.write(" if (ajaxRequest != null) {\n");
399: writer
400: .write(" ajaxRequest.open(\"GET\", uri, false);\n");
401: writer.write(" ajaxRequest.send(null);\n");
402: writer.write(" if (ajaxRequest.status == 200) {\n");
403: writer
404: .write(" window.execScript(ajaxRequest.responseText, \"javascript\");\n");
405: writer
406: .write(" window.de_danet_an_util_jsf_condAddScript.loaded[uri] = true;\n");
407: writer.write(" return;\n");
408: writer.write(" }\n");
409: writer.write(" }\n");
410: writer.write(" }\n");
411: writer
412: .write(" var scriptRef = document.createElement(\"script\");\n");
413: writer
414: .write(" scriptRef.setAttribute (\"type\", \"text/javascript\")\n");
415: writer.write(" scriptRef.setAttribute (\"src\", uri)\n");
416: writer.write(" if (defer) {\n");
417: writer
418: .write(" scriptRef.setAttribute (\"defer\", \"defer\")\n");
419: writer.write(" }\n");
420: writer
421: .write(" var head = document.getElementsByTagName(\"head\")[0];\n");
422: writer.write(" head.appendChild (scriptRef);\n");
423: writer
424: .write(" window.de_danet_an_util_jsf_condAddScript.loaded[uri] = true;\n");
425: writer.write("}\n");
426: writer.write("\n");
427: // Adding style
428: writer
429: .write("function de_danet_an_util_jsf_condAddStyle (uri) {\n");
430: writer
431: .write(" if (!document.getElementsByTagName(\"head\")) {\n");
432: writer.write(" return;\n");
433: writer.write(" }\n");
434: writer
435: .write(" var head = document.getElementsByTagName(\"head\")[0];\n");
436: writer
437: .write(" var links = head.getElementsByTagName(\"link\");\n");
438: writer.write(" var found = false;\n");
439: writer.write(" for (i = 0; i < links.length; i++) {\n");
440: writer.write(" var child = links[i];\n");
441: writer
442: .write(" if (child.nodeType == 1 "
443: + "&& child.getAttribute(\"rel\") == \"stylesheet\" "
444: + "&& child.getAttribute(\"type\") == \"text/css\" "
445: + "&& child.getAttribute(\"src\") == uri) {\n");
446: writer.write(" found = true;\n");
447: writer.write(" break;\n");
448: writer.write(" }\n");
449: writer.write(" }\n");
450: writer.write(" if(!found) {\n");
451: writer
452: .write(" var styleRef = document.createElement(\"link\");\n");
453: writer
454: .write(" styleRef.setAttribute (\"rel\", \"stylesheet\");\n");
455: writer
456: .write(" styleRef.setAttribute (\"type\", \"text/css\");\n");
457: writer
458: .write(" styleRef.setAttribute (\"href\", uri);\n");
459: writer.write(" head.appendChild (styleRef);\n");
460: writer.write(" }\n");
461: writer.write("}\n");
462: writer.write("\n");
463: // Adding to event
464: writer
465: .write("function de_danet_an_util_jsf_addBodyHandler(event, script) {\n");
466: writer
467: .write(" var dummy=document.getElementById(\"de_danet_an_util_jsf_eventTypeDummy\");\n");
468: writer
469: .write(" var handlersAreFunctions = (typeof dummy.getAttribute(\"onclick\") == 'function');\n");
470: writer
471: .write(" var body = document.getElementsByTagName(\"body\")[0];\n");
472: writer
473: .write(" var oldHandler = body.getAttribute(event);\n");
474: writer.write(" if (handlersAreFunctions) {\n");
475: writer.write(" body.setAttribute(event, function() {\n");
476: writer.write(" if (oldHandler) {\n");
477: writer.write(" oldHandler();\n");
478: writer.write(" }\n");
479: writer.write(" eval(script);\n");
480: writer.write(" });\n");
481: writer.write(" } else {\n");
482: writer.write(" if (oldHandler) {\n");
483: writer
484: .write(" body.setAttribute(event, oldHandler + \";\" + script);\n");
485: writer.write(" } else {\n");
486: writer.write(" body.setAttribute(event, script);\n");
487: writer.write(" }\n");
488: writer.write(" }\n");
489: writer.write("}\n");
490: writer.endElement(HTML.SCRIPT_ELEM);
491: context.getExternalContext().getRequestMap().put(
492: COND_ADD_FUNCTIONS_ADDED, Boolean.TRUE);
493: } catch (IOException e) {
494: throw (IllegalStateException) (new IllegalStateException(e
495: .getMessage())).initCause(e);
496: }
497: }
498:
499: public void addResourceHere(FacesContext context,
500: ResourceHandler resourceHandler) throws IOException {
501: validateResourceHandler(resourceHandler);
502:
503: String path = getResourceUri(context, resourceHandler);
504: ResponseWriter writer = context.getResponseWriter();
505: writer.write(context.getExternalContext().encodeResourceURL(
506: path));
507: }
508:
509: /**
510: * Adds the given Style Sheet at the specified document position. If the
511: * style sheet has already been referenced, it's added only once.
512: */
513: public void addStyleSheet(FacesContext context,
514: ResourcePosition position, Class myfacesCustomComponent,
515: String resourceName) {
516: addStyleSheet(context, position, new MyFacesResourceHandler(
517: myfacesCustomComponent, resourceName));
518: }
519:
520: /**
521: * Adds the given Style Sheet at the specified document position. If the
522: * style sheet has already been referenced, it's added only once.
523: */
524: public void addStyleSheet(FacesContext context,
525: ResourcePosition position, String uri) {
526: doAddStyleSheet(context, getResourceUri(context, uri));
527: }
528:
529: /**
530: * Adds the given Style Sheet at the specified document position. If the
531: * style sheet has already been referenced, it's added only once.
532: */
533: public void addStyleSheet(FacesContext context,
534: ResourcePosition position, ResourceHandler resourceHandler) {
535: validateResourceHandler(resourceHandler);
536: doAddStyleSheet(context, getResourceUri(context,
537: resourceHandler));
538: }
539:
540: private void doAddStyleSheet(FacesContext context, String uri) {
541: addCondAddFunctions(context);
542: try {
543: uri = context.getExternalContext().encodeResourceURL(uri);
544: ResponseWriter writer = context.getResponseWriter();
545: writer.startElement(HTML.SCRIPT_ELEM, null);
546: writer.writeAttribute(HTML.SCRIPT_TYPE_ATTR,
547: HTML.SCRIPT_TYPE_TEXT_JAVASCRIPT, null);
548: writer.write("de_danet_an_util_jsf_condAddStyle(\"" + uri
549: + "\");\n");
550: writer.endElement(HTML.SCRIPT_ELEM);
551: } catch (IOException e) {
552: throw (IllegalStateException) (new IllegalStateException(e
553: .getMessage())).initCause(e);
554: }
555: }
556:
557: public String getResourceUri(FacesContext context,
558: Class myfacesCustomComponent, String resource,
559: boolean withContextPath) {
560: return getResourceUri(context, new MyFacesResourceHandler(
561: myfacesCustomComponent, resource), withContextPath);
562: }
563:
564: public String getResourceUri(FacesContext context,
565: Class myfacesCustomComponent, String resource) {
566: return getResourceUri(context, new MyFacesResourceHandler(
567: myfacesCustomComponent, resource));
568: }
569:
570: /**
571: * Get the Path used to retrieve an resource.
572: */
573: public String getResourceUri(FacesContext context,
574: ResourceHandler resourceHandler) {
575: String uri = resourceHandler.getResourceUri(context);
576: if (uri == null) {
577: return getResourceUri(context, resourceHandler
578: .getResourceLoaderClass(), true);
579: }
580: return getResourceUri(context, resourceHandler
581: .getResourceLoaderClass(), true)
582: + uri;
583: }
584:
585: /**
586: * Get the Path used to retrieve an resource.
587: */
588: public String getResourceUri(FacesContext context,
589: ResourceHandler resourceHandler, boolean withContextPath) {
590: String uri = resourceHandler.getResourceUri(context);
591: if (uri == null) {
592: return getResourceUri(context, resourceHandler
593: .getResourceLoaderClass(), withContextPath);
594: }
595: return getResourceUri(context, resourceHandler
596: .getResourceLoaderClass(), withContextPath)
597: + uri;
598: }
599:
600: /**
601: * Get the Path used to retrieve an resource.
602: */
603: public String getResourceUri(FacesContext context, String uri) {
604: return getResourceUri(context, uri, true);
605: }
606:
607: /**
608: * Get the Path used to retrieve an resource.
609: */
610: public String getResourceUri(FacesContext context, String uri,
611: boolean withContextPath) {
612: if (withContextPath) {
613: return context.getApplication().getViewHandler()
614: .getResourceURL(context, uri);
615: }
616: return uri;
617: }
618:
619: /**
620: * Get the Path used to retrieve an resource.
621: */
622: protected String getResourceUri(FacesContext context,
623: Class resourceLoader, boolean withContextPath) {
624: StringBuffer sb = new StringBuffer(200);
625: sb.append(RESOURCE_VIRTUAL_PATH);
626: sb.append(PATH_SEPARATOR);
627: sb.append(resourceLoader.getName());
628: sb.append(PATH_SEPARATOR);
629: sb.append(getCacheKey(context));
630: sb.append(PATH_SEPARATOR);
631: return getResourceUri(context, sb.toString(), withContextPath);
632: }
633:
634: /**
635: * Verify that the resource handler is acceptable. Null is not valid, and
636: * the getResourceLoaderClass method must return a Class object whose
637: * instances implements the ResourceLoader interface.
638: *
639: * @param resourceHandler
640: */
641: protected void validateResourceHandler(
642: ResourceHandler resourceHandler) {
643: if (resourceHandler == null) {
644: throw new IllegalArgumentException(
645: "ResourceHandler is null");
646: }
647: validateResourceLoader(resourceHandler.getResourceLoaderClass());
648: }
649:
650: /**
651: * Given a Class object, verify that the instances of that class implement
652: * the ResourceLoader interface.
653: *
654: * @param resourceloader
655: */
656: protected void validateResourceLoader(Class resourceloader) {
657: if (!ResourceLoader.class.isAssignableFrom(resourceloader)) {
658: throw new FacesException("Class "
659: + resourceloader.getName() + " must implement "
660: + ResourceLoader.class.getName());
661: }
662: }
663:
664: /**
665: * Adds the given Inline Style at the specified document position.
666: */
667: public void addInlineStyleAtPosition(FacesContext context,
668: ResourcePosition position, String inlineStyle) {
669: logger.warn("Adding inline style at " + position
670: + " not supported");
671: }
672:
673: /**
674: * Adds the given Inline Script at the specified document position.
675: */
676: public void addInlineScriptAtPosition(FacesContext context,
677: ResourcePosition position, String inlineScript) {
678: logger.warn("Adding inline script at " + position
679: + " not supported");
680: }
681:
682: /*
683: * (non-Javadoc)
684: *
685: * @see org.apache.myfaces.renderkit.html.util.AddResource#hasHeaderBeginInfos()
686: */
687: public boolean hasHeaderBeginInfos() {
688: return false;
689: }
690:
691: /*
692: * (non-Javadoc)
693: *
694: * @see org.apache.myfaces.renderkit.html.util.AddResource#isResourceUri
695: */
696: public boolean isResourceUri(HttpServletRequest request) {
697: String path;
698: if (contextPath != null) {
699: path = contextPath + RESOURCE_VIRTUAL_PATH;
700: } else {
701: path = RESOURCE_VIRTUAL_PATH;
702: }
703: return request.getRequestURI().startsWith(path);
704: }
705:
706: /*
707: * (non-Javadoc)
708: *
709: * @see org.apache.myfaces.renderkit.html.util.AddResource#parseResponse
710: * java.lang.String, javax.servlet.http.HttpServletResponse)
711: */
712: public void parseResponse(HttpServletRequest request,
713: String bufferedResponse, HttpServletResponse response)
714: throws IOException {
715: }
716:
717: /* (non-Javadoc)
718: * @see org.apache.myfaces.renderkit.html.util.AddResource#requiresBuffer()
719: */
720: public boolean requiresBuffer() {
721: return false;
722: }
723:
724: /* (non-Javadoc)
725: * @see org.apache.myfaces.renderkit.html.util.AddResource#responseFinished()
726: */
727: public void responseFinished() {
728: }
729:
730: /* (non-Javadoc)
731: * @see org.apache.myfaces.renderkit.html.util.AddResource#responseStarted()
732: */
733: public void responseStarted() {
734: }
735:
736: /* (non-Javadoc)
737: * @see org.apache.myfaces.renderkit.html.util.AddResource#serveResource
738: */
739: public void serveResource(ServletContext context,
740: HttpServletRequest request, HttpServletResponse response)
741: throws IOException {
742: String pathInfo = request.getPathInfo();
743: String uri = request.getContextPath()
744: + request.getServletPath()
745: + (pathInfo == null ? "" : pathInfo);
746: String classNameStartsAfter = RESOURCE_VIRTUAL_PATH + '/';
747:
748: int posStartClassName = uri.indexOf(classNameStartsAfter)
749: + classNameStartsAfter.length();
750: int posEndClassName = uri.indexOf(PATH_SEPARATOR,
751: posStartClassName);
752: String className = uri.substring(posStartClassName,
753: posEndClassName);
754: int posEndCacheKey = uri.indexOf(PATH_SEPARATOR,
755: posEndClassName + 1);
756: String resourceUri = null;
757: if (posEndCacheKey + 1 < uri.length()) {
758: resourceUri = uri.substring(posEndCacheKey + 1);
759: }
760: try {
761: Class resourceLoader = ClassUtils.classForName(className);
762: validateResourceLoader(resourceLoader);
763: ((ResourceLoader) resourceLoader.newInstance())
764: .serveResource(context, request, response,
765: resourceUri);
766: response.flushBuffer();
767: } catch (ClassNotFoundException e) {
768: logger.error("Could not find class for name: " + className,
769: e);
770: response.sendError(HttpServletResponse.SC_NOT_FOUND,
771: "Could not find resource loader class for name: "
772: + className);
773: } catch (InstantiationException e) {
774: logger.error("Could not instantiate class for name: "
775: + className, e);
776: response.sendError(
777: HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
778: "Could not instantiate resource loader class for name: "
779: + className);
780: } catch (IllegalAccessException e) {
781: logger.error("Could not access class for name: "
782: + className, e);
783: response.sendError(HttpServletResponse.SC_FORBIDDEN,
784: "Could not access resourceloader class for name: "
785: + className);
786: } catch (Throwable e) {
787: logger.error("Error while serving resource: " + resourceUri
788: + ", message : " + e.getMessage(), e);
789: response
790: .sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
791: }
792: }
793:
794: /* (non-Javadoc)
795: * @see org.apache.myfaces.renderkit.html.util.AddResource#setContextPath
796: */
797: public void setContextPath(String contextPath) {
798: this .contextPath = contextPath;
799: }
800:
801: /* (non-Javadoc)
802: * @see org.apache.myfaces.renderkit.html.util.AddResource#writeMyFacesJavascriptBeforeBodyEnd
803: */
804: public void writeMyFacesJavascriptBeforeBodyEnd(
805: HttpServletRequest request, HttpServletResponse response)
806: throws IOException {
807: }
808:
809: /* (non-Javadoc)
810: * @see org.apache.myfaces.renderkit.html.util.AddResource#writeResponse
811: */
812: public void writeResponse(HttpServletRequest request,
813: HttpServletResponse response) throws IOException {
814: }
815:
816: /* (non-Javadoc)
817: * @see org.apache.myfaces.renderkit.html.util.AddResource#writeWithFullHeader(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
818: */
819: public void writeWithFullHeader(HttpServletRequest request,
820: HttpServletResponse response) throws IOException {
821: }
822:
823: public void processAppendices(RenderRequest request,
824: RenderResponse response, FacesContext facesContext)
825: throws IOException {
826: // Some components seem to use a deprecated mechnism
827: String myFacesJavascript = (String) request
828: .getAttribute("org.apache.myfaces.myFacesJavascript");
829: if (myFacesJavascript != null) {
830: response.getWriter().println(myFacesJavascript);
831: }
832: }
833:
834: }
|