ddraper

Under The Hood of the Surf Updates

Blog Post created by ddraper on Mar 5, 2013

Introduction

In a previous post I described a simpler approach to constructing a page. This post will delve deeper into exactly what is happening to render that page, how the dependency analysis works and what ultimately ends up on the page.

 

 

Page Rendering

The URL “/share/page/dp/ws/my-new-page” was used to access the page. Once this request is mapped to the Share application the following occurs:

    1. /share/page” is Spring MVC dispatcher servlet context
    2. /share/page/dp/ws/my-new-page” is matched to the template-uri “{pageid}/ws/{webscript}” (“dp” is the id of a page)
    3. Surf maps the “dp” page to the Template-Instance “share-template
    4. The “share-template” is an instance of the “share-template-type” template which (unlike the standard Share page template) is rendered by the WebScript “full-page.get
    5. The FreeMarker template for “full-page.get” sets up the skeleton of the page (including all the standard Share JavaScript variables and resources) and also creates a region the Surf Dojo bootstrap WebScript. It creates the Component and Region for the WebScript passed as the “webscript” argument in the template-uri
    6. Surf binds the Components to the Regions and renders them.
    7. The controller for the WebScript passed on the URL creates a JSON model defining the structure of the page
    8. The WebScript template uses the <@processJsonModel> directive to convert that JSON model into JavaScript, CSS, HTML and i18n resources that are automatically loaded into the page.


If you view the source of a page you should see the Dojo bootstrap configuration and a request to load the main “dojo.js” bootstrap module.


Screenshot of page source showing Dojo bootstrap

If you inspect the resources loaded into the page (e.g. in the Web Developer Tools or Firebug) you should also see the following files imported:

    • “/share/res/890525bee5cf4228da1a0e45b491ed.css”
    • “/share/res/40ef2558ae7e1d0a225e277cc6672d0.js”
    • “/share/res/js/surf/b4dc46167c64e043e227df402bf7bc1.js”


The names of the resources are MD5 checksums generated from the resource contents. This allows the browser to cache each resource indefinitely to avoid the need to download it again. Should the contents of the page change (e.g. an update to an individual modules source code or the page model being customized) then the resource contents will change, a new checksum will be generated and the browser will download the updated version.

 

 

Screenshot of Web Development Tools showing resources imported

 

“/share/res/890525bee5cf4228da1a0e45b491ed.css”

This contains all of the CSS resources referenced by widgets included in the page. Note that images are directly encoded into the resource to avoid additional HTTP requests.

 

Screenshot showing Web Development Tools showing aggregated CSS resource

 

“/share/res/40ef2558ae7e1d0a225e277cc6672d0.js”

This contains the messages object construction code and the call to instantiate the main “page” widget (shown highlighted). Note that it is declaring a requirement on the final resource loaded – it is requesting our dynamically built resource that includes all the dependencies.

 

Screenshot showing Web Development Tools with Page widget instantiation

 

“/share/res/js/surf/b4dc46167c64e043e227df402bf7bc1.js”

This is the layer that has been built through dynamic dependency analysis. Note how it contains core Dojo modules that have been identified as dependencies.

 

Screenshot showing Web Development Tools showing dependencies source

 

Dependency Analysis Beans

If you look in the “spring-surf-services-context.xml” file (this is included in “spring-surf-<n>.<n>.<n>.jar”) you’ll find a number of new bean definitions. Of these new beans the “dojo.dependency.handler” defines the main class for analysing the dependencies on a page. It has a property called “dependencyRules” that is a list of all the individual dependency handling beans. By default the following beans are referenced:

    • “define.dojo.js.dependency.rule”
    • “define.dojo.css.dependency.rule”
    • “define.dojo.widgets.dependency.rule”
    • “define.dojo.i18n.dependency.rule”
    • 'define.dojo.non.amd.dependency.rule'


The purpose of each bean is to analyse the source code of each widget and identify one or more of 4 different types of dependency to include in the page:

    • Text (e.g. HTML templates)
    • JavaScript (both AMD and non-AMD modules)
    • CSS
    • i18n properties


    All of these beans extend the “org.springframework.extensions.surf.DojoDependencyRule” class and use regular expressions configured in the bean context to identify the different dependencies. The “dojo.dependency.handler” passes the minified source of each module to each rule processor to identify additional dependencies (this means that the Regular Expression doesn't need to address whitespace characters or comments).

     

    It’s possible to either reconfigure the regular expressions for the beans provided or create and add entirely new beans to the “dependencyRules” list to satisfy your specific requirements.

     

    Dependencies are recursively processed until they have all been analysed. As each module is analysed it is added to a cache and if it is referenced again the cached version will be used. Surf does not allow a module to be analysed twice so even if circular dependencies are declared it will not fall into an infinite loop.

     

    Even if it is not possible to identify all of the dependencies it is not a major problem. Ultimately Surf is just making an attempt to construct a layer containing all of the resources required for that page. If a resource is missing then the Dojo loader will simply asynchronously request it. In fact if you view the source on Share pages you will notice that the occasional AMD module is still getting asynchronously requested – there’s obviously a little bit of tightening up we could do on the dependency analysis expressions!

     

    Page Widget Configuration

    By default Share uses the “alfresco/core/Page” module as the root widget on the page (the default in a “vanilla” Surf application is “surf/core/Page” which is a much simpler version). However it is possible to update the “surf.xml” configuration to use any widget that you would prefer – simply update the “page-widget” element to map to whatever widget you’d like to use.

     

    Summary

    Hopefully this post has gone some way to explaining how Surf performs dependency analysis of the widgets included in a page in order to construct a build layer that can then be requested on the page. Although this series of posts relates specifically to Alfresco the Dojo dependency analysis code is part of Surf and as such could be used in independent applications.

    Outcomes