|
||||||||||
PREV CLASS NEXT CLASS | FRAMES NO FRAMES | |||||||||
SUMMARY: NESTED | FIELD | CONSTR | METHOD | DETAIL: FIELD | CONSTR | METHOD |
java.lang.Objectorg.apache.myfaces.renderkit.html.util.AddResource2
org.apache.myfaces.component.html.util.StreamingAddResource
public class StreamingAddResource
This is a utility class to render link to resources used by custom components.
This enables a JSF component that is within a page to register scripts and css stylesheets that it needs, and have them added to the page output. When multiple components in a page registers a need for the same script or stylesheet multiple times, only one reference is output in the page.
The default DefaultAddResources implementation achieves this by buffering the complete page in memory until after rendering is complete, then post-processing the page; this implementation does not require buffering the output but does have some limitations.
To enable the use of this implementation, the web.xml of the application must set property ????
For references to external resources (javascript files or css files), the url rendered into the page has the special format:
{contextPath}/faces/myFacesExtensionResource/ {resourceLoaderName}/{cacheKey}/{resourceURI}Where:
As specified in the base AddResource interface, most methods come in two flavours: one that takes an explicit ResourceHandler parameter (so can be used by "user" code) and one that implicitly uses a ResourceHandler that serves only Tomahawk resources (ie is intended for use only by Tomahawk components). For the tomahawk-specific methods, the standard MyFacesResourceHandler is used, which in turn uses the standard MyFacesResourceLoader. However for resources that must be cached and served in a separate request (see below) the custom StreamingResourceHandler is used, which uses StreamingResourceLoader.
The DefaultAddResource implementation inserts javascript file references into the head section of the generated page. This streaming implementation cannot do that, so it inserts them into the body of the page instead. This is not technically valid; according to the HTML spec references to external javascript files should be in the HEAD only. However all modern browsers do support this. There may be some corner cases where this does result in different behaviour of the page.
The DefaultAddResource implementation inserts css file references into the head section of the generated page by post-processing the page output after all components have finished rendering. This streaming implementation cannot do that, and no browser supports references to stylesheets from within the HTML page body. Therefore this class implements a workaround: it expects that every page will always emits a single CSS link to a "virtual page" from its HEAD section and then handles the later request for that virtual page by serving any resources that really should have been embedded in the head of the original page. When the page uses the t:documentHead tag to rite the HEAD tags of the page, this link is emitted automatically. This does unfortunately mean that use of this StreamingAddResource always results in an extra GET request per page. It also means that there needs to be an application-scoped cache that holds per-request cached data, which introduces some issues regarding "cleanup" of the cache entries. See javadoc of method addStyleLoaderHere() for more details.
Nested Class Summary | |
---|---|
protected static interface |
StreamingAddResource.PositionedInfo
|
protected static interface |
StreamingAddResource.StreamablePositionedInfo
|
protected static interface |
StreamingAddResource.WritablePositionedInfo
|
Field Summary | |
---|---|
protected String |
_contextPath
|
protected static org.apache.commons.logging.Log |
log
|
protected static org.apache.commons.logging.Log |
logSend
|
Fields inherited from interface org.apache.myfaces.renderkit.html.util.AddResource |
---|
BODY_END, BODY_ONLOAD, HEADER_BEGIN |
Constructor Summary | |
---|---|
StreamingAddResource()
|
Method Summary | |
---|---|
void |
addInlineScriptAtPosition(javax.faces.context.FacesContext context,
ResourcePosition position,
String inlineScript)
Adds the given Inline Script at the specified document position. |
void |
addInlineStyleAtPosition(javax.faces.context.FacesContext context,
ResourcePosition position,
String inlineStyle)
Adds the given Inline Style at the specified document position. |
void |
addJavaScriptAtPosition(javax.faces.context.FacesContext context,
ResourcePosition position,
Class myfacesCustomComponent,
String resourceName)
Insert a [script src="url"] entry into the document header at the specified document position. |
void |
addJavaScriptAtPosition(javax.faces.context.FacesContext context,
ResourcePosition position,
Class myfacesCustomComponent,
String resourceName,
boolean defer)
Insert a [script src="url"] entry into the document header at the specified document position. |
void |
addJavaScriptAtPosition(javax.faces.context.FacesContext context,
ResourcePosition position,
ResourceHandler resourceHandler)
Adds the given Javascript resource to the document header at the specified document positioy by supplying a resourcehandler instance. |
void |
addJavaScriptAtPosition(javax.faces.context.FacesContext context,
ResourcePosition position,
ResourceHandler resourceHandler,
boolean defer)
Adds the given Javascript resource at the specified document position. |
void |
addJavaScriptAtPosition(javax.faces.context.FacesContext context,
ResourcePosition position,
String uri)
Insert a [script src="url"] entry into the document header at the specified document position. |
void |
addJavaScriptAtPosition(javax.faces.context.FacesContext context,
ResourcePosition position,
String uri,
boolean defer)
Adds the given Javascript resource at the specified document position. |
void |
addJavaScriptAtPositionPlain(javax.faces.context.FacesContext context,
ResourcePosition position,
Class myfacesCustomComponent,
String resourceName)
Adds the given Javascript resource at the specified document position. |
void |
addJavaScriptHere(javax.faces.context.FacesContext context,
Class myfacesCustomComponent,
String resourceName)
Insert a [script src="url"] entry at the current location in the response. |
void |
addJavaScriptHere(javax.faces.context.FacesContext context,
ResourceHandler resourceHandler)
Insert a [script src="url"] entry at the current location in the response. |
void |
addJavaScriptHere(javax.faces.context.FacesContext context,
String uri)
Insert a [script src="url"] entry at the current location in the response. |
void |
addJavaScriptHerePlain(javax.faces.context.FacesContext context,
String uri)
Insert a [script src="url"] entry at the current location in the response. In constrast to the other methods this will not encode the url. |
void |
addJavaScriptToBodyTag(javax.faces.context.FacesContext context,
String javascriptEventName,
String addedJavaScript)
|
void |
addResourceHere(javax.faces.context.FacesContext context,
ResourceHandler resourceHandler)
|
void |
addStyleLoaderHere(javax.faces.context.FacesContext context,
Class myfacesCustomComponent)
Hack to allow pages to register CSS stylesheet files or inline CSS commands. |
void |
addStyleSheet(javax.faces.context.FacesContext context,
ResourcePosition position,
Class myfacesCustomComponent,
String resourceName)
Adds the given Style Sheet at the specified document position. |
void |
addStyleSheet(javax.faces.context.FacesContext context,
ResourcePosition position,
ResourceHandler resourceHandler)
Adds the given Style Sheet at the specified document position. |
void |
addStyleSheet(javax.faces.context.FacesContext context,
ResourcePosition position,
String uri)
Adds the given Style Sheet at the specified document position. |
protected String |
getAbsoluteUri(javax.faces.context.FacesContext context,
String uri)
|
protected long |
getCacheKey(javax.faces.context.FacesContext context)
Return a value used in the {cacheKey} part of a generated URL for a resource reference. |
protected StreamingThreadManager.HeaderInfoEntry |
getHeaderInfoEntry()
|
protected String |
getResourceUri(javax.faces.context.FacesContext context,
Class resourceLoader,
boolean withContextPath)
Get the Path used to retrieve an resource. |
String |
getResourceUri(javax.faces.context.FacesContext context,
Class myfacesCustomComponent,
String resource)
|
String |
getResourceUri(javax.faces.context.FacesContext context,
Class myfacesCustomComponent,
String resource,
boolean withContextPath)
Return a URI that can be embedded into an HTML page to reference a resource from a Tomahawk jarfile. |
String |
getResourceUri(javax.faces.context.FacesContext context,
ResourceHandler resourceHandler)
Get the Path used to retrieve an resource. |
String |
getResourceUri(javax.faces.context.FacesContext context,
ResourceHandler resourceHandler,
boolean withContextPath)
Get the Path used to retrieve an resource. |
String |
getResourceUri(javax.faces.context.FacesContext context,
String uri)
Get the Path used to retrieve an resource. |
String |
getResourceUri(javax.faces.context.FacesContext context,
String uri,
boolean withContextPath)
Get the Path used to retrieve an resource. |
protected StreamingAddResource.PositionedInfo |
getScriptInstance(javax.faces.context.FacesContext context,
String uri,
boolean defer)
|
boolean |
hasHeaderBeginInfos()
check there is something to write to the header |
boolean |
hasHeaderBeginInfos(javax.servlet.http.HttpServletRequest request)
|
boolean |
isResourceUri(javax.servlet.ServletContext servletContext,
javax.servlet.http.HttpServletRequest request)
|
void |
parseResponse(javax.servlet.http.HttpServletRequest request,
String bufferedResponse,
javax.servlet.http.HttpServletResponse response)
Parses the response to mark the positions where code will be inserted |
boolean |
requiresBuffer()
return true if you require the complete response buffered |
void |
responseFinished()
called when the response has finished |
void |
responseStarted()
called when the response start |
void |
responseStarted(Object context,
Object request)
|
protected void |
sendError(javax.servlet.http.HttpServletResponse response,
int errorCode,
String errorText)
|
void |
serveResource(javax.servlet.ServletContext context,
javax.servlet.http.HttpServletRequest request,
javax.servlet.http.HttpServletResponse response)
|
void |
setContextPath(String contextPath)
set the context path of the web-app |
protected void |
validateResourceHandler(ResourceHandler resourceHandler)
Verify that the resource handler is acceptable. |
protected void |
validateResourceLoader(Class resourceloader)
Given a Class object, verify that the instances of that class implement the ResourceLoader interface. |
void |
writeMyFacesJavascriptBeforeBodyEnd(javax.servlet.http.HttpServletRequest request,
javax.servlet.http.HttpServletResponse response)
Writes the javascript code necessary for myfaces in every page, just befode the closing </body> tag |
void |
writeResponse(javax.servlet.http.HttpServletRequest request,
javax.servlet.http.HttpServletResponse response)
Writes the response |
void |
writeWithFullHeader(javax.servlet.http.HttpServletRequest request,
javax.servlet.http.HttpServletResponse response)
Add the resources to the <head> of the page. |
Methods inherited from class java.lang.Object |
---|
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait |
Field Detail |
---|
protected static final org.apache.commons.logging.Log log
protected static final org.apache.commons.logging.Log logSend
protected String _contextPath
Constructor Detail |
---|
public StreamingAddResource()
Method Detail |
---|
public void setContextPath(String contextPath)
AddResource
public void addJavaScriptHere(javax.faces.context.FacesContext context, Class myfacesCustomComponent, String resourceName) throws IOException
Example: when customComponent is class example.Widget, and resourceName is script.js, the resource will be retrieved from "example/Widget/resource/script.js" in the classpath.
IOException
public void addJavaScriptHere(javax.faces.context.FacesContext context, String uri) throws IOException
uri
- is the location of the desired resource, relative to the base
directory of the webapp (ie its contextPath).
IOException
public void addJavaScriptHerePlain(javax.faces.context.FacesContext context, String uri) throws IOException
AddResource
uri
- is the location of the desired resource, relative to the base
directory of the webapp (ie its contextPath).
IOException
public void addJavaScriptHere(javax.faces.context.FacesContext context, ResourceHandler resourceHandler) throws IOException
context
- resourceHandler
- is an object which specifies exactly how to build the url
that is emitted into the script tag. Code which needs to generate URLs in ways
that this class does not support by default can implement a custom ResourceHandler.
IOException
public void addResourceHere(javax.faces.context.FacesContext context, ResourceHandler resourceHandler) throws IOException
IOException
protected void validateResourceHandler(ResourceHandler resourceHandler)
resourceHandler
- protected void validateResourceLoader(Class resourceloader)
resourceloader
- public void addJavaScriptAtPosition(javax.faces.context.FacesContext context, ResourcePosition position, ResourceHandler resourceHandler)
Use this method to have full control about building the reference url to identify the resource and to customize how the resource is written to the response. In most cases, however, one of the convenience methods on this class can be used without requiring a custom ResourceHandler to be provided.
If the script has already been referenced, it's added only once.
Note that this method queues the javascript for insertion, and that the script is inserted into the buffered response by the ExtensionsFilter after the page is complete.
public void addJavaScriptAtPosition(javax.faces.context.FacesContext context, ResourcePosition position, Class myfacesCustomComponent, String resourceName)
The resource is expected to be in the classpath, at the same location as the specified component + "/resource".
Example: when customComponent is class example.Widget, and resourceName is script.js, the resource will be retrieved from "example/Widget/resource/script.js" in the classpath.
public void addJavaScriptAtPositionPlain(javax.faces.context.FacesContext context, ResourcePosition position, Class myfacesCustomComponent, String resourceName)
AddResource
public void addJavaScriptAtPosition(javax.faces.context.FacesContext context, ResourcePosition position, Class myfacesCustomComponent, String resourceName, boolean defer)
defer
- specifies whether the html attribute "defer" is set on the
generated script tag. If this is true then the browser will continue
processing the html page without waiting for the specified script to
load and be run.public void addJavaScriptAtPosition(javax.faces.context.FacesContext context, ResourcePosition position, String uri)
uri
- is the location of the desired resource, relative to the base
directory of the webapp (ie its contextPath).public void addJavaScriptAtPosition(javax.faces.context.FacesContext context, ResourcePosition position, String uri, boolean defer)
public void addJavaScriptToBodyTag(javax.faces.context.FacesContext context, String javascriptEventName, String addedJavaScript)
public void addJavaScriptAtPosition(javax.faces.context.FacesContext context, ResourcePosition position, ResourceHandler resourceHandler, boolean defer)
public void addStyleSheet(javax.faces.context.FacesContext context, ResourcePosition position, Class myfacesCustomComponent, String resourceName)
public void addStyleSheet(javax.faces.context.FacesContext context, ResourcePosition position, String uri)
protected String getAbsoluteUri(javax.faces.context.FacesContext context, String uri)
public void addStyleSheet(javax.faces.context.FacesContext context, ResourcePosition position, ResourceHandler resourceHandler)
public void addInlineStyleAtPosition(javax.faces.context.FacesContext context, ResourcePosition position, String inlineStyle)
public void addInlineScriptAtPosition(javax.faces.context.FacesContext context, ResourcePosition position, String inlineScript)
public String getResourceUri(javax.faces.context.FacesContext context, Class myfacesCustomComponent, String resource, boolean withContextPath)
AddResource
This method is intended for internal use by the Tomahawk project only, and will not serve resources for other projects (unless a custom AddResource implementation has been configured). Non-tomahawk code should use the variants that take an explicit ResourceHandler class.
Parameter myfacesCustomComponent is a tomahawk class that the resource is associated with. The resource is then expected to be in the classpath in the same package as the specified class (or a subpackage).
Parameter resource is a path relative to the .class file of the specified myfacesCustomComponent class. Param withContextPath controls whether the webapp name is prefixed to the generated url.
public String getResourceUri(javax.faces.context.FacesContext context, Class myfacesCustomComponent, String resource)
public String getResourceUri(javax.faces.context.FacesContext context, ResourceHandler resourceHandler)
public String getResourceUri(javax.faces.context.FacesContext context, ResourceHandler resourceHandler, boolean withContextPath)
public String getResourceUri(javax.faces.context.FacesContext context, String uri)
public String getResourceUri(javax.faces.context.FacesContext context, String uri, boolean withContextPath)
protected String getResourceUri(javax.faces.context.FacesContext context, Class resourceLoader, boolean withContextPath)
protected long getCacheKey(javax.faces.context.FacesContext context)
Caching in browsers normally works by having files served to them include last-modified and expiry-time http headers. Until the expiry time is reached, a browser will silently use its cached version. After the expiry time, it will send a "get if modified since {time}" message, where {time} is the last-modified header from the version it has cached.
Unfortunately this scheme only works well for resources represented as plain files on disk, where the webserver can easily and efficiently see the last-modified time of the resource file. When that query has to be processed by a servlet that doesn't scale well, even when it is possible to determine the resource's last-modified date from servlet code.
Fortunately, for the AddResource class a static resource is only ever accessed because a URL was embedded by this class in a dynamic page. This makes it possible to implement caching by instead marking every resource served with a very long expiry time, but forcing the URL that points to the resource to change whenever the old cached version becomes invalid; the browser effectively thinks it is fetching a different resource that it hasn't seen before. This is implemented by embedding a "cache key" in the generated URL.
Rather than using the actual modification date of a resource as the cache key, we simply use the webapp deployment time. This means that all data cached by browsers will become invalid after a webapp deploy (all the urls to the resources change). It also means that changes that occur to a resource without a webapp redeploy will not be seen by browsers.
public boolean isResourceUri(javax.servlet.ServletContext servletContext, javax.servlet.http.HttpServletRequest request)
public void serveResource(javax.servlet.ServletContext context, javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws IOException
IOException
protected void sendError(javax.servlet.http.HttpServletResponse response, int errorCode, String errorText) throws IOException
IOException
public boolean hasHeaderBeginInfos(javax.servlet.http.HttpServletRequest request)
public void parseResponse(javax.servlet.http.HttpServletRequest request, String bufferedResponse, javax.servlet.http.HttpServletResponse response)
public void writeMyFacesJavascriptBeforeBodyEnd(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws IOException
IOException
public void writeWithFullHeader(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws IOException
IOException
public void writeResponse(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws IOException
IOException
protected StreamingAddResource.PositionedInfo getScriptInstance(javax.faces.context.FacesContext context, String uri, boolean defer)
public boolean requiresBuffer()
AddResource
protected StreamingThreadManager.HeaderInfoEntry getHeaderInfoEntry()
public void responseStarted()
AddResource
public void responseFinished()
AddResource
public void responseStarted(Object context, Object request)
responseStarted
in class AddResource2
public boolean hasHeaderBeginInfos()
AddResource
public void addStyleLoaderHere(javax.faces.context.FacesContext context, Class myfacesCustomComponent) throws IOException
As described in the class javadocs, the "streaming" approach for resources has problems when it comes to stylesheet links or inline stylesheet commands. These MUST go in the HEAD section of a page, but by the time a component is being rendered the HEAD section is long gone. The DefaultAddResource class can solve this because it buffers the page, but here a different approach is needed.
This method should be called during rendering of the HEAD section of a page. For example, the t:documentHead tag (DocumentHeadRenderer) calls this automatically. A link tag of type CSS is written to the response, pointing at a virtual page "header.css" which does not actually exist.
During rendering of the page body, component renderers may register inline CSS or CSS files. This info is just cached in the user session. After the page has been sent to the remote browser, the browser will then make a request to the virtual "header.css" page which this class intercepts and then serves up the resources needed by the page.
Note that the link to the virtual page must always be rendered, as at this time we do not know whether the body of the page will contain components that need css resources or not. If no component did need CSS resources, then a zero-sized response is returned. And the value can change on each request, depending on which components are rendered or not, so a "requestId" is embedded into the url, making the url change for every request. This requestId is also used to find the relevant cached resources that need to be served (if any).
The url is generated using the StreamingResourceHandler (ie StreamingResourceLoader is the class embedded in the url). This means that when the browser fetches this resource, the StreamingResourceLoader is invoked. It in turn extracts the requestId from the parameter and serves any "head" resources that were registered for the original page.
Note that JSF2.0 solves this issue by having components queue "system events" during the "build tree" phase of rendering. Tomahawk could possibly provide a framework to allow its own classes to do this for JSF1.2. But for JSF1.1 there is no "build tree" phase so this approach is the only possibility.
context
- myfacesCustomComponent
-
IOException
|
||||||||||
PREV CLASS NEXT CLASS | FRAMES NO FRAMES | |||||||||
SUMMARY: NESTED | FIELD | CONSTR | METHOD | DETAIL: FIELD | CONSTR | METHOD |