Friday, December 30, 2011

Efficient component tree traversal in JSF

Everybody knows about findComponent() and invokeOnComponent() methods in JSF to find a component and doing something with it. The first method expects a special search expression and the second one component's clientId. They belong to UIComponent class, so that any UIComponent can acts as a start point in the view hierarchy. But what is about if we want to find many components at some start point in the view hierarchy which match certain conditions? We can apply getChildren() rekursive, but we wish us a convenient method for tree traversal. Tree traversal refers to the process of visiting each node in a component tree data structure. There is an efficient method in JSF accomplishing this task - visitTree()
public boolean visitTree(VisitContext context, VisitCallback callback)
context is an instance of VisitContext class that is used to hold state relating to performing a component tree visit. callback is an instance of VisitCallback whose visit method will be called for each node visited. Let's start with a practical example. Our task is to find and gather all components implementing EditableValueHolder. Such components implement resetValue() method which allows to reset component's submitted, locale values, etc. to an un-initialized state. Furthermore, another requirement is to skip sub-tree traversal for a component which should be not rendered (don't visit sub-components). In other words we want to find only editable components to be rendered. Assume, the start component calls target
UIComponent target = ...
It can be got in an action / event listener by event.getComponent() or somewhere else by findComponent().

We are ready now to create a special EditableValueHoldersVisitCallback in order to find all mentioned above sub-components and call visitTree on the target component with this callback. EditableValueHoldersVisitCallback gathers sub-components we want to reset.
EditableValueHoldersVisitCallback visitCallback = new EditableValueHoldersVisitCallback();
target.visitTree(VisitContext.createVisitContext(FacesContext.getCurrentInstance()), visitCallback);

// iterate over found sub-components and reset their values
List<EditableValueHolder> editableValueHolders = visitCallback.getEditableValueHolders();
for (EditableValueHolder editableValueHolder : editableValueHolders) {
    editableValueHolder.resetValue();
}
Callback looks like this one
public class EditableValueHoldersVisitCallback implements VisitCallback {

    private List<EditableValueHolder> editableValueHolders = new ArrayList<EditableValueHolder>();

    @Override
    public VisitResult visit(final VisitContext context, final UIComponent target) {
        if (!target.isRendered()) {
            return VisitResult.REJECT;
        }

        if (target instanceof EditableValueHolder) {
            editableValueHolders.add((EditableValueHolder) target);
        }

        return VisitResult.ACCEPT;
    }

    public List<EditableValueHolder> getEditableValueHolders() {
        return editableValueHolders;
    }
}
Result VisitResult.REJECT indicates that the tree visit should be continued, but should skip the current component's subtree (remember our requirement?). VisitResult.ACCEPT indicates that the tree visit should descend into current component's subtree. There is also VisitResult.COMPLETE as return value if the tree visit should be terminated.

Some components extending UINamingContainer override the behavior of visitTree(). For instance, UIData overrides this method to handle iteration correctly.

Tuesday, December 27, 2011

Maven plugin for web resource optimization - advanced settings

In the last post I have introduced a new Maven plugin resources-optimizer-maven-plugin. This post is about two advanced features. Aggregation tag has a sub-tag subDirMode. This is an aggregation per sub-folder. Assume, JavaScript / CSS files are grouped by sub-folders which represent software modules. Resources of each module can be optimized separately in an easy way. As output we would have aggregated files per sub-folder. Names of aggregated files are the same as their folder names where they are placed. Files are aggregated in the lexicographic order. The following pictures demonstrate this approach.

Original source files below sub-folder keyfilter:


Aggregated file in target below sub-folder keyfilter:


This can be achieved with the following resources set
<resourcesSet>
    <includes>
        ...
        <include>keyfilter/**</include>
        ...
    </includes>
    <aggregations>
        <aggregation>
            <inputDir>...</inputDir>
            <subDirMode>true</subDirMode>
        </aggregation>
    </aggregations>
</resourcesSet>
The second feature is a preparation of uncompressed resources. Sometimes you have to build / deliver compressed and uncompressed resources. In JSF you can use then uncompressed resources if ProjectStage is "Development". RichFaces has e.g. this handy feature. resources-optimizer-maven-plugin allows to define several aggregation tags, e.g. one for compressed and one for uncompressed resources. The following picture demonstrates this approach.

Two folders, for compressed (primefaces-extensions) and uncompressed (primefaces-extensions-uncompressed) resources:


That can be achieved with this resources set
<resourcesSet>
    <includes>
        ...
    </includes>
    <aggregations>
        <aggregation>
            <inputDir>${resources.dir.compressed}</inputDir>
            ...
        </aggregation>
        <aggregation>
            <withoutCompress>true</withoutCompress>
            <inputDir>${resources.dir.uncompressed}</inputDir>
            ...
        </aggregation>
    </aggregations>
</resourcesSet>

<properties>
    <resources.dir.compressed>
        ${project.build.directory}/classes/META-INF/resources/primefaces-extensions
    </resources.dir.compressed>
    <resources.dir.uncompressed>
        ${project.build.directory}/classes/META-INF/resources/primefaces-extensions-uncompressed
    </resources.dir.uncompressed>
</properties>
Output directory for uncompressed resources should be created first. Put maven-resources-plugin with phase generate-resources in front of resources-optimizer-maven-plugin (phase prepare-package).
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-resources-plugin</artifactId>
    <executions>
        <execution>
            <id>copy-resources</id>
            <phase>generate-resources</phase>
            <goals>
                <goal>copy-resources</goal>
            </goals>
            <configuration>
                <outputDirectory>${resources.dir.uncompressed}</outputDirectory>
                <resources>
                    <resource>
                        <directory>${project.basedir}/src/main/resources/META-INF/resources/primefaces-extensions</directory>
                    </resource>
                </resources>
            </configuration>
        </execution>
    </executions>
</plugin>
These were examples for an JAR project. The same is also valid for WAR (web) projects of course.

Monday, December 19, 2011

New Maven plugin for web resource optimization

Two months ago I released the first version of a new Maven plugin for web resource optimization. This plugin was created as a part of the PrimeFaces Extensions project. What is the PrimeFaces Extensions project? Thomas Andraschko and me have launched this young project with the aim of extending JSF library PrimeFaces. What does extending mean? We don't have "extends SomePrimeFacesComponent" in the code. It doesn't make sense to copy-&-paste or overwrite existing PrimeFaces code. PrimeFaces is a rapidly evolving component set and costs to maintain the entire code from release to release would be too high. Extending means, we use core functionality of PrimeFaces like core.js and CoreRenderer.java. Well. What for components and other stuff we have is not the topic of this post. This is a topic for next post(s). I just would like to appeal to everybody having JSF / PrimeFaces expert knowledge to contact us. Any help would be appreciated. We have great ideas and plans, but Thomas and me are full time worker and involving in various daily projects, so that time for PrimeFaces Extensions is very sparse. Even worse in my case. I can only work 15-20% for JSF in my daily work currently. So, everybody with experience in JSF / PrimeFaces can join this open source project.

Maven plugin for resource optimization can compress and merge JavaScript and CSS files very efficiently. It produces better results than all plugins I know. The new Maven plugin combines Google Closure Compiler (for JavaScript files) and YUI Compressor (for CSS files). The new plugin is highly configurable and allows some nice features which don't exist in other plugins. I would like to mention some highlights:
  • Flexible resource sets describing resources to be processed.
  • Settings for compilation and warning levels for Google Closure Compiler. Defaults are SIMPLE_OPTIMIZATIONS and QUIET.
  • Aggregation with or without compression.
  • Suffix for compressed / aggregated files if needed.
  • SubDirMode for files aggregation. Resources of each software module can be optimized separately in an easy way. It makes sense e.g. for component / widget development because you can say "merge files for component A to one big file and don't merge files for component B".
  • Possiblity to define a file to be prepended to the aggregated file.
  • Possiblity to build compressed and uncompressed resources at the same time. PrimeFaces community already asked about uncompressed resources for development stage. RichFaces and some other libs can deliver uncompressed resources if the ProjectStage is "Development". We have also done this for PrimeFaces Extensions by means of this plugin. A special JSF ResourceHandler can stream down uncompressed resources to the browser for the ProjectStage "Development". Never crypto-messages like "e is undefined" anymore!
I have tried to write a more or less comprehensive documentaion. Simple check it! We already use this plugin with success in 6-8 JAR and WAR projects. I have also drawn a comparison diagram for PrimeFaces: size in bytes without compression, with yuicompressor-maven-plugin and with our resources-optimizer-maven-plugin. And I have prepared a configuration for PrimeFaces to make changes easy. More details in the online documentation. We would be glad to see the resources-optimizer-maven-plugin in PrimeFaces! It's worth. This plugin is available in the Maven central repository. In the meantime I have released a new 0.2 version. Feedback is welcome. Thanks.

The next Maven plugin I'm planning to write is a CDK (component development kit) for PrimeFaces components (low prio, depends on free time). PrimeFaces has already one, but only for internal use. Futhermore, PrimeFaces's plugin doesn't generate component's source files under source folder (they are not under version control) and has an expensive configuration. The new CDK will be configurable via name conventions and avoid expensive configuration. The technique for that will be Java Emitter Templates (JET) from Eclipse Modeling Framework. Here is again - every help is appreciated. We have some code styles, so that our code is clean and optimized. But it's not a problem at all to follow them, even in a remote team. I'm convinced, the work in a distributed team can be very efficient.

Friday, December 16, 2011

Install Windows VHDs on Linux for testing websites with Internet Explorer

Need to test websites in multiple Internet Explorer versions under Linux / Mac OSX? No problem. Microsoft created free Windows Virtual PC VHDs with the purpose of allowing web designers to test websites in Internet Explorer 7-9. There is a script ievms simplifying the installation. But before you have to install the latest VirtualBox. You can do this e.g. via Package Manager (Ubuntu), Yast (OpenSuse) or via Linux terminal
 
sudo apt-get install virtualbox-4.1
 
The next step is the installation of VirtualBox Extension Pack. This is necessary for USB 2.0 devices, shared folders, better screen resolution, etc. The best way is to download and install the package for the VirtualBox Extension Pack manually.
 
wget http://download.virtualbox.org/virtualbox/4.1.6/Oracle_VM_VirtualBox_Extension_Pack-4.1.6-74713.vbox-extpack
VBoxManage extpack install Oracle_VM_VirtualBox_Extension_Pack-4.1.6-74713.vbox-extpack
 
Open VirtialBox tnen and go to File > Preferences > Extensions tab. Click there the "add" button and browse to the VirtualBox Extension Pack. Now you need to install curl (tool to transfer data from or to a server) and unrar (command-line applications for extracting RAR archives). In Ubuntu, install them using the command below.
 
sudo apt-get install curl unrar
 
To download and run the mentioned above script, use the following command in a terminal.
 
curl -s https://raw.github.com/xdissent/ievms/master/ievms.sh | bash
 
The above command will download Windows VHDs for IE7, IE8 and IE9. If you only need one specific Internet Explorer version, e.g. IE9, you can run:
 
curl -s https://raw.github.com/xdissent/ievms/master/ievms.sh | IEVMS_VERSIONS="9" bash
 
At this point, the download will start and it will take a while. The VHD archives are massive and can take hours to download. Each version is installed into a subdirectory of ~/.ievms/vhd/. At the end you can delete the downloaded archives (*.exe and *.rar files) below this folder if you want to free up some space. You are ready to test websites in IE7-IE9 now. The password for all VMs is "Password1". The picture below shows a VirtualBox with IE9.


The next picture shows that I set shared folders to exchange data between Windows 7 in VM and my Kubuntu 11.10 (Oneiric Ocelot).


You can start your web application on Linux. But how to access a running web application on Windows (VirtualBox)? Linux has a command tool ifconfig to configure, control and query TCP/IP network interface parameters. Open a terminal and type ifconfig. Find an IP address you Linux mashine is accessible from. I have e.g. 192.168.254.21 for wlan0 interface. You are able to call now
 
http://192.168.254.21:8080/...
 
in Interner Exloper on Windows. The IP address is like a localhost for your web application running on Linix. Remaning part behind this address is the same on both OS.