Wednesday, December 16, 2009

How ICEFaces Loads Its CSS GIF files

How ICEFaces loads its CSS gif Files

ICEFaces has the dataPaginator tag. It uses icons(gif files) which the user can click on to go back or forward to another page.
The following is an example:
<ice:dataPaginator id="dataScroll_1" for="data" fastStep="3"
 pageCountVar="pageCount" pageIndexVar="pageIndex" paginator="true"
 paginatorMaxPages="20" styleClass="formBorderHighlight">
 <f:facet name="first">
  <ice:graphicImage id="firstpage_1"
   url="./xmlhttp/css/xp/css-images/arrow-first.gif"
   style="border:none;" title="first page" />
 </f:facet>
 ......
</ice:dataPaginator>

Notice that the graphicImage tag uses the url attribute to load the gif file. Where is this gif file ?

The answer is that it is in the icefaces.jar file. More specifically, it is at the location
com/icesoft/faces/resources/css/xp/css-images/arrow-first.gif in the jar.

Now the next question is: how does the servlet find it?

The url starts with xmlhttp. From servlet mapping in web.xml, we know this url will be handled by the same icefaces servlet com.icesoft.faces.webapp.xmlhttp.PersistentFacesServlet that handles other icefaces jsp files. But how does it work exactly?

Debugging the application, I found that the following class is used when the PersistentFacesServlet handles the url:

public class ResourceServer implements Server
{
  public ResourceServer(Configuration configuration, MimeTypeMatcher mimeTypeMatcher, FileLocator fileLocator)
  {
    PathDispatcherServer pathDispatcher = new PathDispatcherServer();
    pathDispatcher.dispatchOn(".*xmlhttp\\/javascript-blocked$", new RedirectOnJSBlocked(configuration));
    pathDispatcher.dispatchOn(".*xmlhttp\\/.*\\/.*\\.js$", new CacheControlledServer(new ServeJSCode()));
    pathDispatcher.dispatchOn(".*xmlhttp\\/css\\/.*", new CacheControlledServer(new ServeCSSResource(mimeTypeMatcher)));
    pathDispatcher.dispatchOn(".*xmlhttp\\/blank$", new ServeBlankPage());
    pathDispatcher.dispatchOn(".*", new FileServer(fileLocator, mimeTypeMatcher));
    if(configuration.getAttributeAsBoolean("compressResources", true))
       dispatcher = new CompressingServer(pathDispatcher);
    else
       dispatcher = pathDispatcher;
  }
  
  public void service(Request request) throws Exception
  {
     dispatcher.service(request);  
  }

  public void shutdown()
  {
    dispatcher.shutdown();
  }
  
  private Server dispatcher;

Note that for the url pattern .*xmlhttp/css/*, it uses ServeCSSResource, which is defined as follows:
public class ServeCSSResource
    implements Server
{
    public ServeCSSResource(MimeTypeMatcher mimeTypeMatcher)
    {
        loader = getClass().getClassLoader();
        matcher = mimeTypeMatcher;
    }
    public void service(Request request)
        throws Exception
    {
        final String path = request.getURI().getPath();
        String file = path.substring(path.lastIndexOf("css/") + 4, path.length());
        final InputStream in = loader.getResourceAsStream("com/icesoft/faces/resources/css/" + file);
        if(in == null)
            request.respondWith(NotFoundHandler.HANDLER);
        else
            request.respondWith(new ResponseHandler() {
                public void respond(Response response)
                    throws Exception
                {
                    response.setHeader("Content-Type", matcher.mimeTypeFor(path));
                    response.writeBodyFrom(in);
                }
                throws IOException
            {
                super();
            }
            }
);
    }
    public void shutdown()
    {
    }
    private static final String Package = "com/icesoft/faces/resources/css/";
    private ClassLoader loader;
    private MimeTypeMatcher matcher;
}
The constant string "Package" defined in this class reveals the secret behind the scene. The "Package" variable defines exactly the directory path to find the CSS gif files.