Archive for October, 2008

In spite of the fact that both Roger and I arrived at AjaxWorld carrying our east coast colds, we managed to make it through our “JSF and Ajax” presentation.Couple of snapshots from the presentation:

Andy and Jeremy Geelan

Roger and Andy

Two other EG members presented on JSF-related topics. Ted Goddard (ICEsoft) presented on ICEfaces’s support for Ajax Push on the iPhone.Ted’s presentation included a fun live demo where iPhone-enabled audience members were able to get into an ICEfaces-based chat room and push messages to each other.Jeremy Grelle (SpringSource) presented on Spring’s Ajax support, including coverage of Spring Faces and an interesting demo of “progressive enhancement”.

One other notable JSF-related presentation:Wesley Hales (JBoss/Red Hat) provided a great introduction to the state of affairs in the portal/portlet world.Wesley discussed how both ICEfaces and RichFaces approach portlet integration, and also covered how emerging standards will make life easier for Ajax/portlet development.

Other JSF old-timers (and old friends) John Fallows and Jonas Jacobi also presented, though not on JSF.John and Jonas focused on HTML 5, in particular on the WebSocket API and how Kaazing’s offerings make it possible to take advantage of HTML 5 features today.

With several EG members actually in the same place at the same time (an unusual occurrence), we took advantage of the opportunity to hold an informal face-to-face meeting.Roger, Ted, Jeremy, John, special guest attendee Blake Sullivan (Oracle) and I met to discuss declarative solutions for Ajax, an area that we are trying to shape up for the JSF 2.0 release.

I am heading out to California this week to attend AjaxWorld West in San Jose. In addition to having the opportunity to check out all sorts of interesting sessions, I will also be presenting with Roger Kitain from Sun MicroSystems. Our presentation is JSF and Ajax: Past, Present and Future. As you might imagine, we’ll be taking a look at how Ajax-based solutions have evolved in the JSF world and what we expect to come next in JSF 2.0.

Of course, any trip to the Bay Area wouldn’t be complete without my usual visit to Oracle HQ. Once AjaxWorld wraps up I’ll be spending the remainder of the week at HQ, catching up with friends and teammates both old and new.

In Hello, Library Partitioning! we took our first peek at the new ADF Faces JavaScript Library Partitioning feature. In order to support this functionality, ADF Faces introduces two new configuration files. In this entry, we’ll see how the metadata defined by these configuration files is used to optimize the JavaScript footprint of ADF Faces.

ADF Faces loads the library partitioning configuration files at application initialization time, starting with the “feature” configuration file. Since the goal is to allow custom component authors to leverage the library partitioning system for their own JavaScript code, we use a tried and true technique for locating all available feature configuration files: ClassLoader.getResources(). Given a path to a resource file, ClassLoader.getResources() scans all jars on the class path and returns references to any matching files that are found. This mechanism should be familiar to JSF users: it is the same approach used to locate faces-config.xml files.

In order to locate feature configuration files, ADF Faces searches for all “adf-js-features.xml” files in the “META-INF” directory. This allows custom component authors that want to participate in JavaScript library partitioning to simply drop a “META-INF/adf-js-features.xml” file into their jar. No explicit registration is necessary. Any feature configuration files found are loaded into memory and merged into a common data structure. Of course, ADF Faces provides its own feature configuration file so that its own features are always available.

In the case of the “partition” configuration, we only have one configuration file per application. ADF Faces looks for this file, named “adf-js-partitions.xml”, in the “WEB-INF” directory of the web application. If no such file is found, ADF Faces falls back to a default partition configuration that it provides. The default partitioning is meant to provide reasonable behavior for most applications, though application developers are welcome to optimize as needed for their application.

Now that we’ve got all of this information available, how is it actually put to use?

During the render traversal, ADF Faces collects information about which JavaScript features are required by the page. At the end of the traversal, the complete set of JavaScript features required by the (rendered) page contents is known. Once we know the set of required JavaScript features, we use our partition configuration to map this to the set of required partitions. And given the set of required partitions, we simply render HTML <script> references to these partitions, just before the end of the HTML document.

That’s it! By using this technique, we end up pulling in just those “chunks” of JavaScript code that are needed by the page, thus reducing the total amount of code that needs to be downloaded/loaded.

In Almost Getting To The Point, we took a look at requirements for a solution to the JavaScript size vs # of round trips dilemma. In this entry, we’ll see how those requirements translate into an actual solution as implemented by the ADF Faces “JavaScript library partitioning” feature. In this solution, we have three types of JavaScript artifacts:

1. The JavaScript “class”

This is a coherent set of code contained in a single file, which typically implements the equivalent of a class. For example, the “oracle/adf/view/js/component/rich/layout/AdfRichPanelStretchLayout.js” file contains the JavaScript code for the af:panelStretchLayout’s JavaScript component class.

In our solution the class is the smallest unit of content that can be aggregated.

2. The JavaScript “feature”

Since component authors do not want application developers to have hard dependencies on JavaScript file names or paths, we introduce the concept of the JavaScript “feature”. The JavaScript feature is a collection of JavaScript files that are associated with a logical identifier that describes the feature.

For example, the af:panelStretchLayout component implementation is contained within two JavaScript files:

These two files are grouped into the “AdfRichPanelStretchLayout” feature. This allows application developers to refer to the af:panelStretchLayout code by the name “AdfRichPanelStretchLayout”, without having to worry about the number, name or locations of files that are used to implement this feature.

3. The JavaScript “partition”

JavaScript “features” are usually small units of JavaScript code – typically a few related files that combine to implement a single area of functionality (such as a user interface component). JavaScript “partitions” group JavaScript features into larger collections with the goal of influencing download size/number of round trips

For example, it’s not uncommon for applications that use the af:panelStretchLayout component to also use the af:panelSplitter component. These two components often go hand in hand. Assuming we have both an “AdfRichPanelStretchLayout” feature and an “AdfRichPanelSplitter” feature, these two features would be a good candidate to be placed in the same JavaScript library partition.

Looking at the division of responsibilities, we have:

Component authors place their JavaScript code in files.

Component authors are responsible for grouping these files into logical “features”.

Application developers are responsible for further grouping these features into larger “partitions”.

In order to support these responsibilities, the ADF Faces JavaScript library partition solution introduces two new configuration files: the JavaScript feature configuration file and the JavaScript partition configuration file. Let’s take a look at each of these.

ADF Faces provides a feature configuration file for its own set of components/framework features and also provides a default partition configuration so that application developers are not required to define a partitioning themselves.

So, now that we’ve got all of this wonderful information about our JavaScript code base, what do we do with it? That’s the topic of the next entry of course. :-)

In Does Size Matter? we concluded that, while aggregating JavaScript code is generally a good thing, at a certain point we might need to back off and consider breaking up our code. At the moment ADF Faces is at one end of the size vs. round trip spectrum. The number of round trips is optimized by combining all JavaScript code into a single library (single round-trip). At the other end of the spectrum, we could optimize total downloaded code size by breaking out each JavaScript “class” into its own library (many round trips). Clearly some happy medium – a balance between size and round trips – is needed.

Perhaps the ideal solution would be a GWT-like approach. We could consider analyzing JavaScript code usage to determine the bare minimum code requirements down to the method level. While this would certainly lead to a highly optimized solution, it does have one drawback. This is hard! In a traditional JEE/JSP/JSF application, JavaScript code lives in a number of places, some of which are not exactly easy to identify. JavaScript code may be bundled up into jar files or war files (not too hard to identify), or may live inside of JSPs or other templates (slightly harder to identify), or may be generated programmatically via Java code (very hard to identify). That’s not to say that this approach is impossible or not worthwhile; this is clearly an idea that is worth pursuing. However, given the scope of such a project, a solution along these lines was simply not in the cards for our current release.

So, if we aren’t able to perform code analysis to produce a minimal application-specific JavaScript library, what are our other options? Let’s start by considering some requirements that we would like our solution to meet.

1. Aggregation. We want a solution that allows us to group JavaScript files into larger “chunks”. At the moment, the ADF Faces JavaScript code is delivered in one big chunk. We want to break this up into smaller chunks that can be delivered to the browser independently.

2. Specificity. Any given chunk should only be downloaded/loaded on pages that need the chunk. So, for example, if the JavaScript code for the af:richTextEditor component lives in its own chunk, this code should only be pulled in on pages which use the af:richTextEditor component.

3. Cacheability. Chunks should be cacheable. That is, the contents of the chunks should not vary from page to page. If a chunk is required by both page A and page B, it should not be necessary to download the chunk a second time when navigating from page A to page B.

4. Tunability. The number and content of the chunks should be configurable at the application level. Different applications have different JavaScript usage patterns and should be able to optimize the chunks (ie. optimize the size vs. round-trip trade-off) accordingly.

5. Abstraction. When defining the contents of a chunk, the application developer should not be required to reference the physical JavaScript file names or locations. For example, when defining a chunk that includes the JavaScript code for the af:richTextEditor component, the application developer should not need to know that the af:richTextEditor implementation relies on code located in the “oracle/adfinternal/view/js/laf/dhtml/rich/AdfDhtmlRichTextEditorPeer.js” file. File names and locations are implementation details that are easily changed/re-factored. Instead of relying on file names, the application developer should reference the code via some logical identifier that is guaranteed not to change. This protects both the component author (freedom to change/re-factor) and the application developer (freedom from breaking when pesky component authors make changes).

6. Defaults. Some default set of chunks should be provided so that application developers are not forced to go to the trouble of defining their own chunk layout.

7. Ordering. The solution should ensure that chunks are loaded in an order that honors code dependencies between chunks. If chunk A depends on code in chunk B, chunk A should be loaded before chunk B.

8. Extensibility. Custom components should be able to participate in the solution. That is, it should be possible to chunk custom component code in the same manner as ADF Faces framework/component code.

Assuming we’ve hit the key requirements (and hopefully we did, as I am running out of serious-sounding words to describe each requirement), these requirements translate into a fairly straightforward solution. Which of course is the topic of my next entry. :-)

When it comes to JavaScript code size, of course it does. Too much code leaves users waiting impatiently for JavaScript libraries to download. Or worse, not waiting. Even in cases where users have a warm browser cache, the overhead of loading large libraries into the JavaScript engine can slow down page view/transition times, making your web application feel less responsive.

Size is an issue for any web application framework that includes a client-side component. ADF Faces is no exception. In this blog entry we’ll take a look at how ADF Faces attempts to deal with its (growing) JavaScript code base.

In ADF Faces each JavaScript “class” lives in its own file. This simplifies the development process, making it easier to manage a large code base. However, with over 400 JavaScript classes/files, serving each of these up in separate requests is not an option.

Fortunately, the Apache TrinidadResourceLoader architecture comes to the rescue. In particular, Trinidad’s AggregatingResourceLoader makes it easy to merge multiple files into a single resource at runtime. ADF Faces uses this to merge *all* of its JavaScript code into a single library.

2. Minimize

When working with a large JavaScript code base, code maintainability is an important consideration. Developers should be able to freely add comments/whitespace and to use descriptive variable/method names. Of course, such verbosity is not appreciated when it comes to download size, and comments, white spaces and descriptive names are wasted on the JavaScript engine.

Again, Apache Trinidad provides a solution here. Trinidad includes a JavaScript obfuscator that handily strips comments, whitespaces and obfuscates/minimizes local variable names. ADF Faces integrates the Trinidad JavaScript obfuscator into its own build process. As a result, writing comments, or whitespace, or using descriptive variable names in the ADF Faces JavaScript code base is not a crime but instead is encouraged.

3. Compress

Minimization is a good start, but not a good place to stop. When it comes to optimizing download sizes, compression is king. The current generation of web browsers all support gzip-compressed JavaScript payloads. We typically use Oracle Web Cache to help with this.

This strategy seemed reasonable when it was first put it in place two and half years ago. At that point, our minimized JavaScript code base maxed out at around 540K, which compressed down to roughly 120K. Not exactly small, but manageable.

Of course, ADF Faces development has not stood still over the last two and a half years. Our component set has doubled. A range of new architectural features has been added. As you might imagine, new components and features translates into more JavaScript code.

How much more? As of the recent JDeveloper 11g production release, our total minimized code size is now in the vicinity 1.4M. After compression this comes back down to 320K.

Okay, definitely not small.

What lessons can we take away here?

Well, one lesson is that compression is our friend. JavaScript code compresses nicely – compressing can cut down the download size (and thus download time) by ~75%. This is good.

On the flip side, a tough lesson is that client-based components/features come at a price: increased JavaScript code size. At a certain point, the simple aggregate/minimize/compress strategy of JavaScript code management seems no longer up to the task.

What to do? In the next entry, we’ll consider alternate strategies for managing a large JavaScript code base.