General Interface is an open source project hosted by the Dojo Foundation

GI Contributor Blog Blog from Aug 24, 2009

  2009/08/24
Coming in 3.9 - Cross-Domain GI
Last Changed by Jesse Costello-Good, Aug 24, 2009 10:56
Labels: crossdomain, gi, 3_9, ant, build

I am excited to announce that nightly builds of GI 3.9 now include the functionality to load the GI runtime and your application from different domains. This will allow you more flexibility when deploying your GI application and open up some exciting new possibilities, such as:

  • Hosting the GI runtime on a CDN (content delivery network) for improved load times. Feel free to test it out on this build on our Amazon CDN: http://cdn.generalinterface.org/gi/3.9.0a440/.
  • Using a single runtime installation for several GI applications, even ones hosted on different domains. Each app will use the GI runtime saved in the browser's cache if the user has visited just one of the other applications.
  • Including GI applications in blogs and other CMS's where hosting files is not permitted.
  • Allowing a system administrator to install a single instance of the GI runtime to be shared across an organization, simplifying application deployment for users.

The improvement is described in JIRA issue GI-681.

GI provided two main challenges when implementing this new cross-domain functionality. The first was its reliance on XML/XSL data files. As we all know there is no way of loading arbitrary data, such as XML, across domains with the typical browser security settings. One of the simplest workarounds to this problem relies on the fact that JavaScript can be loaded across domains. JSONP and similar techniques essentially turn a data file into a JavaScript file with one statement. The statement calls a callback function, passing in the file's data as a parameter.

This is the technique that I used for GI. However, JSONP typically uses a server to generate the JavaScript code from data hosted on the server. GI is a server-less architecture so I had to come up with another implementation. What I did was to augment the GI build process so that every XML or XSL file in the GI runtime is compiled and copied to a JSONP-like JavaScript file. For example, the file JSX/locale/messages.xml,

<data jsxnamespace="propsbundle" locales="">

  <locale>
    <!-- jsx3.lang.ClassLoader -->
    <record jsxid="boot.env_reset" jsxtext="Error redefining JSX environment parameter {0} from ''{1}'' to ''{2}''."/>
    <record jsxid="boot.class_err" jsxtext="Could not load class {0}."/>
    <record jsxid="boot.class_ex" jsxtext="Error loading class {0}: {1}"/>
    <record jsxid="boot.class_undef" jsxtext="Loaded JavaScript file {0} but class {1} was not defined."/>
...

becomes

jsx3.net.Request.xdr(
  "jsx:/locale/messages.xml", 
  "<data jsxnamespace=\"propsbundle\" locales=\"\">\n\n  <locale>\n    <record jsxid=\"boot.env_reset\" jsxtext=\"Error redefining JSX environment parameter {0} from ''{1}'' to ''{2}''.\"/>\n    <record jsxid=\"boot.class_err\" jsxtext=\"Could not load class {0}.\"/>\n    <record jsxid=\"boot.class_ex\" jsxtext=\"Error loading class {0}: {1}\"/>\n    <record jsxid=\"boot.class_undef\" jsxtext=\"Loaded JavaScript file {0} but class {1} was not defined.\"/>\n ...   ");

This process is completely automated by the build process, which uses the new JsEncodeTask task to compile the XML/XSL resources. By default it is turned off since it would lead to a certain amount of code and data bloat. To enable it you must set the build.gi.xd build property to true, either in build/user.properties or on the command line,

$> ant -Dbuild.gi.xd=true

All this allows the GI runtime to be loaded from a domain other than the one hosting the GI launch page. But what if you want to host your application on a separate domain as well? You'll need to process the XML and XSL resources in your application in a similar manner. Luckily, the same Ant task that GI uses is available for you to use on your own application. In addition, there is a command line interface for the encoder that comes with the source distribution of GI.

This is how to use the command line interface:

$> cd WORKSPACE/JSXAPPS/myApp
$> find . -name "*.xml" -or -name "*.xsl" | xargs \
   sh GISRC/build/tools/bin/jsencode.sh -user ../ 

The second significant challenge was the synchronous loading of resources. The "JSONP" technique for cross-domain data access only works asynchronously. Historically, GI has loaded many things synchronously. However, over the past few releases and especially with GI 3.7 and the debut of the AMP architecture, GI has become less and less dependent on synchronous loading. At this point the framework does not require synchronous loading at all and an app as complex as Builder can load completely asynchronously.

Unfortunately GI still exposes several APIs that rely on synchronous loading. These APIs are still available for developers to use in their applications. However, they will break if you try to deploy your application cross-domain using the jsencode.sh tool. Examples of synchronous APIs are:

  • jsx3.require()
  • jsx3.net.Request.send() (when open() is called with bAsync=false)
  • jsx3.app.Model.load()
  • jsx3.xml.Document.load()
  • jsx3.app.Cache.getOrOpenDocument()
  • jsx3.lang.ClassLoader.loadJSFileSync()
  • XML URL (of jsx3.xml.Cacheable) when XML Async is false
  • Persistence = Referenced (not Referenced Async)

I expect that the trickiest synchronous API to avoid will be jsx3.require, aka "dynamic class loading." Even if you don't use this method directly your application may depend on it. When you load a component file that contains objects of classes that aren't loaded, those classes are loaded synchronously so that Model.load or Model.loadXML can return synchronously.

There are several possible workarounds to synchronous class loading:

  • Use a custom build of GI that pre-loads all GI classes that the application uses
  • Use the AMP architecture and plug-in pre-requisites to declare the class requirements of each plug-in
  • Use the new 3.9 method jsx3.requireAsync()

I am still working on new APIs to 3.9 in order to make it easier to write a completely asynchronous app. Async loading offers more benefit than just cross-domain loading; it is also much more performant than sync loading. Stay tuned for more improvements before 3.9 goes GA.

Posted at 24 Aug @ 11:00 AM by Jesse Costello-Good | 0 Comments