Thursday, February 16, 2012

ICEfaces Advanced Component Environment

Purpose

The ICEfaces Advanced Component Environment was conceived as a new platform for creating JSF 2+ components, that would solve many of the development inefficiencies and inconsistencies in the previous platform. Many lessons were learned in ICEfaces 1.x components, as they migrated from being JSF 1.1 to JSF 1.1 + 1.2 components. Best practices were found, after many different approaches had been taken by different developers over time.

Some key differences planned for the new components were:

1. Instead of being thin on the client with all processing being done server side, as ICEfaces 1.x components are, the new components would use javascript to do as much processing as possible on the client, and only interact with the server as necessary. There will always be arguments for each approach, as each has its place, so being able to provide both, rounds out our capabilities.

2. Components would rely on many different resources: third party javascript we're wrapping, our integration javascript, structural css, theme css, images and sprite images.

Already we were headed in a direction where the components would have most of their logic in the javascript, and the java should be quite minimalist, with some component specific code, but most everything else could be logically reduced to some declarations that would favour code generation.

There is a lot of wiring code and markup in JSF components, with JSP Tag files, Facelets TagHandlers, the faces-config.xml, taglib.xml. Our previous platform was truly a pain to maintain, and was based on xml files which provided no real type safety, error checking or reporting. We wanted something that would solve all those problems, while also generating all the boilerplate component code, such as property getter/setter methods and state saving code.

JSF standard getter/setter methods aren't really good at all. They don't allow for the component property having a different value for each row of the UIData or UIRepeat that they're in. As well, they can't be used with a combination of the property being tied to a ValueExpression as well as the setter method being called directly. This can happen when the Renderer's decode method needs to set state in the component, or when applications use a component binding or get access to the component via FacesEvents' source. Some properties have an inherently read/write aspect to them, and some have an inherently read only aspect. It seemed best to standardise exactly how that would be implemented. Past disparate implementations exhibited different side effects and bugs.


Debate

There was a large debate between three main approaches to specifying each component:

1. Continue using xml files, but expand what they would state about a component, so that the generator would contain no hard-coded exceptions, like the old one had been rife with.

2. Add annotations directly to the component or renderer, which would be used to generate the component code. This seemed ideal, where we just write the unique component code, and then add annotations to generate the redundant parts.

3. Add separate classes that would have the annotations, which would be separate from the actual component and renderer classes. This was a compromise between the other two options, where the separate annotated classes in theory could be swapped out for xml files, and either declaration format could generate the components.

Right away we favoured annotations in Java files, where the compiler itself could type check most things for us. There were cyclical declaration issues in the concept of annotating either the end component or an abstract super class. We wanted to maintain the option of the Meta classes being completely separate from the resulting component code, so we could make use of that for our IDE integration as well, which we don't release as open source. And it wouldn't be good to clutter the component with info for every single IDE integration, and thereby obscure the core of the component details. The compromise ended up being the best of all worlds.

There was a long process of prototyping the generator with some example components, and continuously pulling code out of the components and pushing the functionality into the generator. JSF 2 was quite the moving target, which really validated the generator concept, as a single change could be made in the generator to immediately affect every component.


Documentation

Documentation is key with ICEfaces components, since the intent behind a property needs to be communicated, along with the implications of different values, and how some properties interact. With modern IDE integration, the documentation needs to be available when editing view definition files as well as when writing bean code and using property getter/setter methods. The ACEnvironment allows for specifying the TLDDoc and JavaDoc in a single place, as well as individually specifying the TLDDoc and the setter JavaDoc and the getter JavaDoc. In practice, having a single place where you can explain a property means it will happen that once, instead of not at all.

The standard TLDDoc doesn't cover aspects like property default values, ClientBehaviors, facets. With ACE, anything in the Meta class can eventually be shown in the TLDDoc and JavaDoc.


Themes, Sprites, Resources

Outside of the generator and Meta files, there's a whole part of the ACEnvironment that provides themeroller standard themes, that creates sprite images from regular images and updates css files to make use of the sprites. There's jQuery and YUI javascript, as well as any other third party javascript that we integrate with. The build process concatenates and minifies the javascript and css files for performance.


Coding Conventions

One huge intangible aspect of the Advanced Components, are the code conventions they use, aided by helper classes. These are derived from many years of experience designing JSF components, all aimed at side-stepping common pitfalls.

The component Base classes contain all of the generated component code, and the concrete sublass of that is the actual component class, which may need to override broadcast(-), queueEvent(-), process*(-) methods. The rest of the java code is in the Renderer, and is comprised of the decode and event queuing logic, as well as the markup and javascript rendering logic. We have moved away from using DOM centric rendering APIs, and instead use the ResponseWriter approach, which ICEfaces internally can use to directly render output to the client, or employ an intermediate server side DOM for optimising transmitted updates. The html is all properly escaped, and the javascript rendering uses a special helper class JSONBuilder that elevates component writers above the level of javascript string concatenation, to a javascript datastructure level, which also employs proper escaping.

On the client end, the javascript dialogs with the Renderer, receiving and transmitting information. Exactly how the javascript functions are called, and how the script tags are placed within the html markup, and what gets ids and what doesn't, are all quite intentionally done. Some of that is to work with IE browsers, and most of it is to facilitate our incremental DOM updates. It's quite common for an Advanced Component to render itself out, and need to be cognoscente of whether the DOM differencing will update the whole component, some sub-section of the html markup, or just the javascript. In many cases, because the javascript adds listeners to the html elements, if one is updated then both should be. Or in other cases we need to ensure that only the javascript is updated and not the html elements. These subtleties can be completely unnoticeable to a non-ICEfaces developer.


Conclusion

ICEfaces 2 ACE and ICEfaces 3 ACE have been a long time coming, with much experience driving the design goals, and many changes in JSF 2.0.x and 2.1.x in both Mojarra and MyFaces continuing to move the goal posts. It's a rich web component library that focusses on client execution, to complement our more server centric ICE component library. As ICEfaces Component Team Leader during its development, I've enjoyed the opportunity to work with my team to develop something so architecturally unique, that overcomes so many challenges, and creates a solid foundation for further development. I'm proud that ACEnvironment is the foundation that ICEmobile is built on, which itself is a revolutionary component library!