Comparing Vaadin with Icefaces

General comparison #

Introduction #

Icefaces is an open source Ajax framework for developing RIA for the web. The applications are structured the same way as JSF applications, but with added Ajax features. In this article there will be a short description of the biggest differences between the IT Mill Toolkit 5.2.0 and Icefaces 1.6.2 based development of RIAs.

Configuration #

Setting up up the Toolkit is simple and straightforward. In Eclipse you create a dynamic web project, import the Toolkit JAR file and write a couple of lines (<servlet> and <servlet-mapping>) in web.xml to define a servlet. After that you can start developing the application.

Icefaces runtime is directly dependable on 10-11 different JAR files. Most of them are common libraries used (e.g. commons-logging.jar, xercesimp.jar) , but also two Icefaces JAR files and two more JSF files. An additional faces-config.xml file is created to get a mapping from the jsp files to one or several backing beans. In web.xml you need to add a couple of <context-param> to find the faces-config.xml file and to define that the state of the JSF application is stored on the server side.

Way of programming #

The developer does not need to think about browser (in)compatibilities, differences etc. In none of the frameworks you need to touch any JavaScript nor HTML, but otherwise the ways to development differ quite a bit. When coding with Toolkit you code almost identically like you would code a standalone Java Swing application. This should be familiar to most Java developers. Icefaces has the page layout in JSP files which are connected to the backing bean(s) that handle the controller function. So when you have made your layout using the XML-tags defined in the Icefaces namespace you need to make sure the backing been has all the used mappings updated accordingly. E.g. variables used in the view need proper getters and setters in the backing bean so that the values can be accessed and modified.

Component comparison #

The basic components using Ajax needed in web applications are found in both frameworks. There are some minor differences though:
  • Toolkit has data binding containers, which can be used as datasources in UI components which automatically (using lazy loading) renders the contents.
  • Icefaces has some effects which can be applied on components, e.g. slide, shake, highlight and a few more.
  • Icefaces has built in drag-and-drop components. This still can be done with Toolkit, though.
  • Toolkit provides an easy way to define a text label content type.
  • Icefaces has created their own set of components, while Toolkit has integrated Googles GWT to render the client side code. Both frameworks allow the creation of custom components when needed.

Migrating #

If you are using JSF pages and consider migrating to a Ajax framework, this is very easily done if you choose Icefaces. Because of the tight integration, not much needs to be done. If you on the other hand don't use JSF, but want to start from scratch, then your choice is fully dependable on your project's needs.

Conclusion #

As a conclusion a couple of pointers could be noticed. The strength of IT Mill Toolkit is that the way of programming is like programming any Swing application. This means that there are no or at least only a few configuration files to keep track of when developing and evolving the internet application. Icafaces uses JSF logic in the application structure and needs views , backing beans and a special configuration which maps these together. When the project grows this can be both a strength and a weakness depending on what the project desires. If you have a large existing JSF based web application the most convenient solution is probably to convert to Icefaces, since they are so tightly coupled. The end result of both of the frameworks are satisfiable and most web applications find what they need from either of them. As mentioned previously, the biggest difference lays in the way of developing and maintaining the application. Should it be mostly in code (Toolkit) or should some configuration files also be involved (Icefaces).

Case study: Simple IMAP Portlet developed using Icefaces and Toolkit #

Background #

The original Icefaces reference project was created as a part of the Project Course taught at Åbo Akademi University. The team of two was assigned to create an IMAP Portlet using any technology suitable for creating Java portlets. The choice fell upon JSF and Icefaces to make the user interface more dynamic with Ajax. At the time JSF and Icefaces were totally new and previousily unknown technologies for both team members. Soon after this one of the team members got hired at IT Mill and it was now possible to make a similar implementation using IT Mill Toolkit instead of JSF/Icefaces. This made it possible to at least to some extent compare the end result and the effectivity of a Java developer when using JSF/Icefaces and IT Mill Toolkit.

The requirements were that a portlet is created which can access a mail account over IMAP and handle the most common tasks. The portlet should be running on Liferay portal, but can not contain any code that restricts usage to only that platform. There were existing IMAP portlet solutions allowing a global IMAP server to be used, but the idea here was to be able to define IMAP servers on a per user basis if needed.

Here is the end results side by side. On the left is the original Icefaces application and to the right you find the Toolkit application.

UML & Design #

The Icefaces project is setup in a way that clearly differentiates backend logic from frontend user interface. In a typical JSF way the view.jspx file is connected to a backing bean, MailController in this case. There is nothing special with this bean, except that it stores the variables and methods used in the JSF tags in the view.jspx file. MailboxBean contains all the mailbox typical logic while the connection package handles all connections to the IMAP and SMTP servers. This is done using the JavaMail API provided by Sun.

The main application is ImapPortlet which extends the Application class. MailStoreContainer handles the actions for a whole mailbox, e.g. connection to IMAP server. MailFolder is quite directly mapped to the IMAPFolder objects provided to ImapPortlet by the connected MailStoreContainer. MailItem is a simple representation of the IMAPMessage of the JavaMail API.

Configuration #

The Icefaces project is setup from the sample project “sample-icefaces-sun-portlet” found on Liferay pages. The web.xml might have some extra parameters not directly required for this project, but still it is considerably larger that the Toolkit projects web.xml. At least context-params and application listeners need to be declared here. The Toolkit project only needs to declare the servlet and after that the Toolkit library will handle the rest.

Some code samples #

Login screen #

This is the beginning of the view.jspx file:
#!xml
<f:view xmlns:f="[http://java.sun.com/jsf/core"]
	xmlns:h="[http://java.sun.com/jsf/html"]
	xmlns:ice="[http://www.icesoft.com/icefaces/component"]
	xmlns:liferay-faces="[http://liferay.com/tld/faces">]
	<ice:outputDeclaration doctypeRoot="HTML"
		doctypePublic="-//W3C//DTD HTML 4.01 Transitional//EN"
		doctypeSystem="[http://www.w3.org/TR/html4/loose.dtd"] />
	<f:loadBundle basename="language" var="msg" />
	<ice:form>
		<ice:outputStyle href="xmlhttp/css/xp/xp-portlet.css" />
		<ice:outputStyle href="./css/imap-portlet_style.css" />
		<ice:panelGrid rendered="#{!MailController.loggedIn}">
			<h3><ice:outputText value="#{msg['imap']}" /></h3>
			<ice:panelGrid columns="2">
				<ice:selectBooleanCheckbox value="#{MailController.imapLoginSecure}"
					partialSubmit="true" />
				<ice:outputText value="#{msg['imap_secure_question']}" />
				<ice:outputText value="#{msg['imap_username']}" />
				<ice:inputText value="#{MailController.imapLoginUsername}" />
				<ice:outputText value="#{msg['imap_password']}" />
				<ice:inputSecret value="#{MailController.imapLoginPassword}"
					redisplay="true" />
				<ice:outputText value="#{msg['imap_host']}" />
				<ice:inputText value="#{MailController.imapLoginHost}" />
				<ice:outputText value="#{msg['imap_port']}" />
				<ice:inputText value="#{MailController.imapLoginPort}" />
			</ice:panelGrid>
			<h3><ice:outputText value="#{msg['smtp']}" /></h3>
			<ice:panelGrid columns="2">
				<ice:selectBooleanCheckbox value="#{MailController.useSmtpLogin}"
					partialSubmit="true" />
				<ice:outputText value="#{msg['smtp_login_question']}" />
				<ice:selectBooleanCheckbox value="#{MailController.useSecureSmtp}"
					partialSubmit="true" disabled="#{!MailController.useSmtpLogin}" />
				<ice:outputText value="#{msg['smtp_secure_question']}" />
				<ice:outputText value="#{msg['smtp_username']}" />
				<ice:inputText value="#{MailController.smtpLoginUsername}"
					disabled="#{!MailController.useSmtpLogin}" />
				<ice:outputText value="#{msg['smtp_password']}" />
				<ice:inputSecret value="#{MailController.smtpLoginPassword}"
					disabled="#{!MailController.useSmtpLogin}" redisplay="true" />
				<ice:outputText value="#{msg['smtp_host']}" />
				<ice:inputText value="#{MailController.smtpLoginHost}" />
				<ice:outputText value="#{msg['smtp_port']}" />
				<ice:inputText value="#{MailController.smtpLoginPort}" />
				<ice:commandButton id="doLogin" type="submit"
					value="#{msg['server_button_do_login']}"
					action="#{MailController.serverLogin}" />
				<ice:commandButton id="cancelLogin" type="submit"
					value="#{msg['server_button_cancel_login']}"
					action="#{MailController.serverLoginCancel}" />
			</ice:panelGrid>
		</ice:panelGrid>
	</ice:form>
</f:view>

The namespaces are defined at the beginning of the file and the tags are added from the desired namespace later on. If the developer wants a normal JSF component he can use <h: ... > or <f: ... > and Ajax enabled components are added with the <ice: ... > tags. Message bundles are automatically inserted in the proper language using the #{msg'key'}. The keys are located in a language_<locale>.properties files. The basename for language files are defined in the <f:loadBundle> tag. To use these, the following has to be added to <faces-config> in faces-config.xml:

#!xml
<application>
	<locale-config>
		<default-locale>en</default-locale>
		<supported-locale>sv</supported-locale>
		<supported-locale>fi</supported-locale>
	</locale-config>
	<message-bundle>
		language
	</message-bundle>
</application>

The values for the <ice:inputText> tags are retrieved from the MailController backing bean using the #{MailController.variablename} JSF syntax. The view uses the proper getters and setters of a variable for retrieving and modifying it's value. It is also possible to define your own CSS file to use for this view in the <ice:outputStyle> tag. When submitting the login information a normal method is called in the backing bean which takes care of the background logic of a login action to the IMAP server.

For an example we might copy-paste the same code in Toolkit:

#!java
    private OrderedLayout initLoginForm() {
        OrderedLayout loginLayout = new OrderedLayout(
                OrderedLayout.ORIENTATION_HORIZONTAL);

        // IMAP
        Panel loginPanel = new Panel("IMAP");
        loginPanel.setStyleName(Panel.STYLE_LIGHT);

        imapUsr = new TextField("Username:");
        imapUsr.setValue(serversInfo.get("imapUsername"));
        loginPanel.addComponent(imapUsr);

        imapPwd = new TextField("Password:");
        imapPwd.setValue(serversInfo.get("imapPassword"));
        imapPwd.setSecret(true);
        loginPanel.addComponent(imapPwd);

        imapHost = new TextField("Host:");
        imapHost.setValue(serversInfo.get("imapHost"));
        loginPanel.addComponent(imapHost);

        imapPort = new TextField("Port:");
        imapPort.setValue(serversInfo.get("imapPort"));
        loginPanel.addComponent(imapPort);
        loginLayout.addComponent(loginPanel);

        imapUseSSL = new CheckBox("Use ssl for imap");
        imapUseSSL.setValue(new Boolean(serversInfo.get("imapUseSSL")));
        loginPanel.addComponent(imapUseSSL);

        // submit button
        OrderedLayout submitLoginLayout = new OrderedLayout(
                OrderedLayout.ORIENTATION_HORIZONTAL);
        submitLoginLayout.setMargin(true);
        submitLoginLayout.setSpacing(true);
        loginButton = new Button("Login");
        loginButton.addListener((Button.ClickListener) this);
        submitLoginLayout.addComponent(loginButton);
        cancelLoginButton = new Button("Empty");
        cancelLoginButton.addListener((Button.ClickListener) this);
        submitLoginLayout.addComponent(cancelLoginButton);

        loginPanel.addComponent(submitLoginLayout);

        // SMTP
        loginPanel = new Panel("SMTP");
        loginPanel.setStyleName(Panel.STYLE_LIGHT);
        smtpUsr = new TextField("Port:");
        smtpUsr.setValue(serversInfo.get("smtpUsername"));
        loginPanel.addComponent(smtpUsr);

        smtpPwd = new TextField("Password:");
        smtpPwd.setValue(serversInfo.get("smtpPassword"));
        smtpPwd.setSecret(true);
        loginPanel.addComponent(smtpPwd);

        smtpHost = new TextField("Host:");
        smtpHost.setValue(serversInfo.get("smtpHost"));
        loginPanel.addComponent(smtpHost);

        smtpPort = new TextField("Port:");
        smtpPort.setValue(serversInfo.get("smtpPort"));
        loginPanel.addComponent(smtpPort);
        loginLayout.addComponent(loginPanel);

        return loginLayout;
    }

The most notable difference is the typical Swing like code with panels and components and the use of listeners.

Changing tab #

If you want to change the selected tab from the backing bean, you first bind the TabSet to a matching PanelTabSet variable tabSet and then you can call tabSet.setSelectedIndex(int index) to make this happen. You need to keep the backing bean and the view in sync during the software's evolution, so that you don't accidentally break it.

view.jspx

#!xml
<ice:panelTabSet styleClass="componentPanelTabSetLayout" id="mainTabs"
	binding="#{MailController.tabSet}"
	rendered="#{MailController.loggedIn}">
	<ice:panelTab label="#{msg['inbox']}" styleClass="portlet-font" id="inboxTab">
	....
	</ice:paneTab>
</ice:panelTabSet>
MailController.java
#!java
private PanelTabSet tabSet = new PanelTabSet();

public void moveToTabIndex(int index) {
	log.trace("Selecting tab "+index+".");
	tabSet.setSelectedIndex(index);
}

Table of mails listed #

For listing the mails in a table we use the <ice:dataTable> tag:
#!xml
<ice:dataTable value="#{MailController.mailList}" var="mail"
	id="maillist" rows="10" columnClasses="selCol,subjectCol,fromCol">
	<ice:column>
		<f:facet name="header">
			<ice:outputText value="#{msg['mail_list_header_select']}" />
		</f:facet>
		<ice:selectBooleanCheckbox value="#{mail.selected}"
			immediate="true" />
	</ice:column>
	<ice:column>
		<f:facet name="header">
			<ice:outputText value="#{msg['mail_list_header_subject']}" />
		</f:facet>
		<ice:commandLink action="#{MailController.openMail}"
				immediate="true">
			<f:param name="activeMailId" value="#{mail['uid']}" />
			<h:outputText value="#{mail.subject}" rendered="#{mail.seen}" styleClass="seenMailSubject" />
			<h:outputText value="#{mail.subject}" rendered="#{!mail.seen}" styleClass="unseenMailSubject" />
		</ice:commandLink>
	</ice:column>
	<ice:column>
		<f:facet name="header">
			<ice:outputText value="#{msg['mail_list_header_from']}" />
		</f:facet>
		<ice:outputText value="#{mail['from']}" />
	</ice:column>
	<ice:column>
		<f:facet name="header">
			<ice:outputText value="#{msg['mail_list_header_date']}" />
		</f:facet>
		<ice:outputText value="#{mail['date']}" />
	</ice:column>
</ice:dataTable>

In the backing bean MailController.getMailList() returns an array of MailBean objects which are automatically iterated through in the view. This way it is only necessary to define what happens on each row. Facets are used to set the titles on the columns. If the subject is clicked MailController.openMail is called which checks the parameter associated with this row's commandLink. Parameters are set with <f:param> inside a <ice:commandLink>.

In the Toolkit case the table datasource is set to a MailFolder which implements Container.Indexed. The tabel component handles the rendering automatically. The only tricky part here is to be able to both select one or more mails for deletion/opening and to get a click-to-open subject. The solution in this case was to set the Tabel to editable and add a field factory for the Table component which made it possible to render a link style Button instead of plain text when the subject was processed at rendering. The item has to return a Boolean object for this to happen, so another property PROPERTY_SUBJECT_LINK is called instead of the String returning PROPERTY_SUBJECT. The normal PROPERTY_SUBJECT is called when opening a mail for reading and then we want just a plain String to be returned.

#!java
        mailTable.setVisibleColumns(new Object[] {
                ImapPortletConstants.PROPERTY_SUBJECT_LINK,
                ImapPortletConstants.PROPERTY_FROM,
                ImapPortletConstants.PROPERTY_DATE });
        mailTable.setEditable(true);

        mailTable.setFieldFactory(new BaseFieldFactory() {

	....

            @Override
            public Field createField(Container container, Object itemId,
                    Object propertyId, Component uiContext) {
                if (propertyId
                        .equals(ImapPortletConstants.PROPERTY_SUBJECT_LINK)) {
                    Button b = new Button((String) container.getItem(itemId)
                            .getItemProperty(
                                    ImapPortletConstants.PROPERTY_SUBJECT)
                            .getValue());
                    b.setStyleName(Button.STYLE_LINK);
                    b.addListener((Button.ClickListener) ImapPortlet.this);
                    b.setData(itemId);
                    if (((MailItem) mailTable.getItem(itemId)).isSeen()) {
                        b.addStyleName("seen");
                    } else {
                        b.addStyleName("unseen");
                    }
                    return b;
                } else {
                    // by returning null, other columns will not be editable
                    return null;
                }
            }

        });

Because the Icefaces version 1.6.2 does not include a dynamic tab set component, the solution in this case was to define five statical non-rendered tabs which are shown and values set for used variables when a mail is opened. This leads to a maximum of five simultaneous tabs to read mails from. In Icefaces 1.7 they have added dynamic tab sets, so it is probably easier to achieve the genuine dynamic functionality with that version.

In Toolkit adding and removing Tabs in the TabSheet is easy and dynamic with the TabSheet.addTab() method.

Lazy loading #

An additional improvement to the original Icefaces application, is that the Toolkit application does not fetch all headers for the mails in a folder while loading a folder. The Icefaces solution fetches all headers in the backend and after that the paginator splits the list into several pages. This could probably have been fixed somehow, but the time and person resources got a bit too limited for this to happen.

In the Toolkit application the container is implemented so that IID are created for all mails, but only if getItem(Object itemId) notices, that now an item is requested whose headers have not been fetched yet, it will fetch the headers from the IMAP server through the JavaMail API. This way the default rending of the Table will fetch 10 headers first and then lazy loading will prefetch 20 more after the rendering is done.

#!java
    public MailItem getItem(Object itemId) {
         if (containsId(itemId)) {
            MailItem item = mails.get(itemId);
            if (item == null) {
                addItemAt(indexOfId(itemId));
                item = mails.get(itemId);
            }
            if (item.getItemProperty(ImapPortletConstants.JAVAMAIL_MESSAGE) == null) {
                try {
                    // fetch message from imap server
                    Folder f = getFolder();
                    if (!f.isOpen()) {
                        f.open(IMAPFolder.READ_WRITE);
                    }
                    IMAPMessage msg = (IMAPMessage) getFolder().getMessage(
                            itemIds.indexOf(itemId) + 1);
                    // add IMAPMessage as property to MailItem object
                    item.addItemProperty(ImapPortletConstants.JAVAMAIL_MESSAGE,
                            new ObjectProperty(msg));
                } catch (MessagingException e) {
                    e.printStackTrace();
                    return null;
                }
            }
            return mails.get(itemId);

        } else {
            return null;
        }
    }

Reflections and comments #

The positive thing about the Toolkit in this project is definitely the more application like programming style. It is somewhat more intuitive at least if you have developed regular Swing applications earlier. Icefaces works ok, but the configuration and mapping between view and backing bean forces the developer to check the variable and method mappings all the time when changing something.

On the other hand the Icefaces made it somewhat simpler to modify the rows of the mail listing table. Using facets it was easy to add a checkbox at every row for multiple selection. Toolkit supports multiple selection, but there the rows needed to be modified to enable both multi selection and one-click opening of a mail.

Icefaces forces the developer to use at least some kind of MVC pattern, while Toolkit lets the developer define himself how he wants to structure the application. This could be both a positive and negative thing. It gives more freedom, but freedom can also lead to messy structure.

Statistics #

#!rst
=========================== ============ ============ =====
**Time report of coding**
-----------------------------------------------------------
**Task**                    **Icefaces** **Toolkit**
**UI**
-----------------------------------------------------------
Use CSS                     2            2.5
Folder tree                 11           4
Maillist paging             10           2
Basic UI                    22           17.75
UI delete mail              6
UI sending mail             2
UI reading mail             30                        Quite complex with dynamic tabs in version 1.6.2, now (probably) easier when dynamic tabs component introduced in Icefaces 1.7.
Reply mail                  5
Ask username/password       16
Show quota                  3
Delete and select                        16.75
Prototype                   7            4
**Subtotal**                **114**      **47**
**Business logic**
-----------------------------------------------------------
Automatic login             18           3
Resource files                           2
Data containers                          46            Contains most backend logic Container <-> Javamail API, these are listed under Icefaces below
Reading mail                14.5
Delete mail                 5
Send mail                   13
Create mailcontroller       3
Create connectionbean       4
Create mailuserbean         0.5
Folder bean backend         13
Mailbean                    1
**Subtotal**                **72**       **51**
**General stuff**
-----------------------------------------------------------
Learn JSF/Toolkit           10           5
Portlet config                           10.5
Code cleanup                14           21.5
Subtotal                    24           37
**Total**                   **210**      **135**      **hours**
=========================== ============ ============ =====



================================ =========== ============= ======
**Code rows**
-----------------------------------------------------------------
**UI**
-----------------------------------------------------------------
view.jspx                        420
MailController.java              1084
NodeUserFolder.java              276
ImapPortlet.java                             896
**Subtotal**                     **1780**    **896**
**Backend Logic**
-----------------------------------------------------------------
ConnectionBean.java              389
MailUserBean.java                109
Properties.java                  33
ImapPortletException.java        11
LoginException.java              11
NetworkConnectionException.java  11
FolderBean.java                  145
MailBean.java                    279
MailboxBean.java                 356
ImapPortletConstants.java                    22
MailEditor.java                              24
MailFolder.java                              426
MailItem.java                                196
MailStoreContainer.java                      317
**Subtotal**                     **1344**    **985**
**Configuration**                                          These might not be fully comparable since the Icefaces config files were copied from an example project.
---------------------------------------------------------- ------
web.xml                          99          21
portlet.xml                      50          37
faces-config.xml                 50
liferay-portlet.xml              27          26
liferay-display.xml              8           7
Subtotal                         234         91
**Total**                        **3358**    **1972**      **LOC**
================================ =========== ============= ======

References #

11 Attachments
20144 Views
Average (2 Votes)
Comments