Closed stevesouth closed 10 years ago
Hi Steve,
An additional consideration here, is that developing an AppCache application is really painful if the AppCache is enabled in development, and so it's critical that all AppCache functionality is short-circuited within development -- I talked about this in a blog post I wrote some time ago: http://blog.caplin.com/2011/02/14/taming-the-app-cache/. As it happens, an AppCache plug-in can probably be implemented by creating a 'bundler', of all things, by implementing the BundlerPlugin
interface so that it returns a manifest within the handleRequest()
method. The interface is as follows:
public interface BundlerPlugin extends TagHandlerPlugin {
String getMimeType();
FileSetFactory getFileSetFactory();
RequestParser getRequestParser();
List<String> generateRequiredDevRequestPaths(BundleSet bundleSet, String locale) throws BundlerProcessingException;
List<String> generateRequiredProdRequestPaths(BundleSet bundleSet, String locale) throws BundlerProcessingException;
void handleRequest(ParsedRequest request, BundleSet bundleSet, OutputStream os) throws BundlerProcessingException;
}
The fact you need to create a 'bundler' to do this is clearly wrong, and may help us extract a subset of this interface for doing more general 'servlet' type stuff like this. Of course, a server-side plugin like this makes it trivial to ensure the manifest always has the correct values, as you can do this by querying the bundlers to determine the set of the resources that the page (and therefore the manifest) must contain.
If you fancy the challenge, this would be a good plug-in to try writing since it should be do-able in a day or two, and would provide good feedback for what we are trying to achieve.
-- Dominic
Here's an example of how a ServletPlugin
interface could be extracted out of the current BundlerPlugin
interface:
public interface ServletPlugin extends Plugin {
RequestParser getRequestParser();
void handleRequest(ParsedRequest request, BundleSet bundleSet, OutputStream os, HttpResponse httpResponse) throws BundlerProcessingException;
}
public interface BundlerPlugin extends ServletPlugin, TagHandlerPlugin {
String getMimeType();
FileSetFactory getFileSetFactory();
List<String> generateRequiredDevRequestPaths(BundleSet bundleSet, String locale) throws BundlerProcessingException;
List<String> generateRequiredProdRequestPaths(BundleSet bundleSet, String locale) throws BundlerProcessingException;
}
Apart from shunting a couple of methods into a new interface, we add an HttpResponse
parameter, which could conceivably allow arbitrary HTTP response headers to be set -- in our case, the Content-Type
so that we can short-circuit the App-Cache in development.
This arrangement also feels nicer, since the bundler interface is now more compatible with the single-responsibility principle; all tag handing and serving now being taken care of elsewhere.
-- Dominic
Would we want ServletPlugin
to also extend TagHandlerPlugin
? The manifest plugin could then be responsible for writing the manifest tag in to the page, it might also choose to only write it out in prod, but not in dev.
Hi Andy, yes I think I'm coming to a similar-ish conclusion (that the AppCache plugin would need to implement both ServletPlugin
and TagHandlerPlugin
), but I'm not sure that ServletPlugin
should therefore extend TagHandlerPlugin
.
I think my original idea that ServletPlugin would introduce HTTP headers is a bad idea though since logical serve requests can happen even when HTTP isn't involved, which is another reason a TagHandlerPlugin
would be perfect for preventing AppCache from happening in dev.
the AppCache plugin would need to implement both ServletPlugin and TagHandlerPlugin
That sounds sensible.
since logical serve requests can happen even when HTTP isn't involved
What about the mime type for the manifest once the app has been exported?
What about the mime type for the manifest once the app has been exported?
Great point! For completely flat-file exports we have no other option but to ensure all files have the correct extension, and hope that the web-server being used knows the Mime-Type for these extensions. For richer exports (like War files) then we will know the Mime-Type anyway, so everything will be fine provided the exported does the right thing.
Are we planning to use a filter to set the mime type in a War or can we just rely on the app server to set the mime type? In both scenarios, web server or app server, config can be added to tell the server what the mime type is based on the extension used.
I hadn't put any thought into it, but think we can take a 'wait and see' approach since we may know more by then.
Oh, BTW, I recently updated all of our UML diagrams so they correlate more closely with reality again, so took the opportunity to split out a ServletPlugin
from BundlerPlugin
at the same time:
We've created a ContentPlugin interface that, at the moment, has the following interface.
public interface ContentPlugin extends Plugin {
String getMimeType();
ContentPathParser getContentPathParser();
void writeContent(ParsedContentPath path, BundleSet bundleSet, OutputStream os) throws BundlerProcessingException;
List<String> getValidDevRequestPaths(BundleSet bundleSet, String locale) throws BundlerProcessingException;
List<String> getValidProdRequestPaths(BundleSet bundleSet, String locale) throws BundlerProcessingException;
}
writeContent is the place where most of the work is done, that's where you would write out the manifest file, probably making calls to other ContentPlugin's getValidDevRequestPaths/getValidProdRequestPaths to get the URL's for bundles etc.
Custom ContentPlugins
will probably also TagHandlerPlugins
so they can be used within index pages via a logical tag (e.g. <@app.manifest@>).
How would I allow my application to use the appcache?
In order for some of your applications files to be cached for offline use you can use the appcache.
https://developer.mozilla.org/en/docs/HTML/Using_the_application_cache
How would I go about augmenting the bladerunner war step to add the required information? Two things need to change.
1) A new tag added into your index.html file
2) The example.appcache file needs to be created, listing the files you want cached. CACHE MANIFEST index.html header.png blah/blah I think the simplest way is to only modify the html tag during deployment and alway have the appcache file available, maybe in unbundled resources. However the second issue is that it is a bit difficult to figure out what you want cached as the bundles / images etc will move and be renamed. As a first step maybe this can be covered by a tutorial but it would be nice to automate it somehow.