Monday, July 16, 2012

Event-based communication in JSF. Old-school approach.

Web applications written in JSF consist of beans which interact among each other. Communication between beans is one of the main design patterns when developing a web application. Sometimes, one bean needs to send events to other beans to inform them about some changes or whatever else. We can normally inject a managed or Spring bean into the property of another bean, so that another bean can notify injected bean directly. Injection is good, but it was not introduced for the purpose of communication. It is far away from a dynamic loosely coupled system where each bean doesn't know about other beans. In a loosely coupled system we need a good event-based communication mechanism. This post will cover two design patterns: Observer / Event Listener and Mediator pattern. These patters are widely used in many web applications nowadays, but they have disadvantages. The system is not really loosely-coupled with them. There are much better and modern approaches. Therefore, I wrote "Old-school approach" in the post name. New-school approaches will be disclosed in the next post.

Observer / Event Listener
We will start with the Observer (also called as Event Listener) pattern. An object, called the subject or observable object, maintains a list of its dependents, called observers, and notifies them automatically of any state changes. In Java there are classes java.util.Observer and java.util.Observable which help to implement this pattern. Other related constructs for event-based communication by means of this pattern are the class java.util.EventObject and the interface java.util.EventListener. Let's start coding. Assume we have a I18N web application and user can choose a language (Locale) somewhere in user settings. Assume we have a bean called UserSettingsForm which is responsible for user settings. Some session scoped beans can keep I18N text / messages, so that when user changes the current languages, a reset of previous text / messages in the last selected language is needed. Firstly, we need a LocaleChangeEvent.
public class LocaleChangeEvent extends EventObject {
    
    Locale locale;

    public LocaleChangeEvent(Object source, Locale locale) {
        super(source);
        this.locale = locale;
    }

    public Locale getLocale() {
        return locale;
    }
}
Secondly, we need an interface LocaleChangeListener.
public interface LocaleChangeListener extends EventListener {
    
    void processLocaleChange(LocaleChangeEvent event);
}
Our UserSettingsForm can manage now instances of type LocaleChangeListener by registring and notifying them.
@ManagedBean
@SessionScoped
public class UserSettingsForm implements Serializable {

    private Locale selectedLocale;
    private List<SelectItem> locales;
    private List<LocaleChangeListener> localeChangeListeners = new ArrayList<LocaleChangeListener>();

    public void addLocaleChangeListener(LocaleChangeListener listener) {
        localeChangeListeners.add(listener);
    }

    public void localChangeListener(ValueChangeEvent e) {
        ...
        // notify listeners
        LocaleChangeEvent lce = new LocaleChangeEvent(this, this.selectedLocale);
        for (LocaleChangeListener lcl : localeChangeListeners) {
            lcl.processLocaleChange(lce);
        }
    }
    ...
}
The method localChangeListener() is an JSF ValueChangeListener and can be applied e.g. in h:selectOneMenu. Every bean which implements LocaleChangeListener should be registered by UserSettingsForm in order to be notified by locale changes.
@ManagedBean
@SessionScoped
public MyBean implements LocaleChangeListener, Serializable {

    // UserSettingsForm can be injected e.g. via @ManagedProperty annotation or via Spring facility
    private UserSettingsForm userSettingsForm;

    @PostConstruct
    public void initialize() {
        userSettingsForm.addLocaleChangeListener(this);
    }

    public void processLocaleChange(LocaleChangeEvent event) {
        // reset something related to I18N data
        ...
    }
}
In terms of Observer pattern the UserSettingsForm is Observable and instances of LocaleChangeListener (like MyBean) are Observers. The discussed pattern comes with some important issues that you need to be aware of. Beans are tightly coupled. There are a lot of manually work to regsiter beans. Beans have to implement defined interfaces. If you have a bean informed by 100 semantic different changes, it has to implement 100 interfaces. It is not possible to notify a subset of registered listeners - always all listeners get notified even if they don't need to be notified. Last but not least - memory management issue. Martin Fowler wrote "Assume we have some screens observing some domain objects. Once we close a screen we want it to be deleted, but the domain objects actually carry a reference to the screen though the observer relationship. In a memory-managed environment long lived domain objects can hold onto a lot of zombie screens, resulting in a significant memory leak."

Mediator
The Mediator pattern improves the event-based communication in comparison to the Observer / Event Listener pattern. With the mediator pattern, communication between objects is encapsulated with a mediator object. Objects no longer communicate directly with each other, but instead communicate through the mediator. This reduces dependencies between communicating objects. We will see how it works for JSF-Spring beans (in examples above were standard managed beans). We will implement a Mediator class to manage the communication between scoped beans. It is important to understand that a bean only can notify another beans having broader scope(s). A view scoped bean can notify view, session and application scoped beans, but not request scoped beans with the narrower scope. Follow this rule to avoid troubles. This is a nature of scoped bean - you might remember that you can always inject a bean of wider scope into a bean of narrower scope, but not vice versa. To start working with Mediator we will introduce two interfaces MediatorEvent, MediatorListener and the centric class Mediator.
public interface MediatorEvent {
    ...
}

public interface MediatorListener {

    public void listenToEvent(MediatorEvent event);
}

public class Mediator implements Serializable {

    private Collection<MediatorListener> collaborators = new HashSet<MediatorListener>();

    public static Mediator getCurrentInstance() {
        // access Mediator bean by JSF-Spring facility
        return ContextLoader.getCurrentWebApplicationContext().getBean("mediator");
    }

    public void fireEvent(MediatorEvent event) {
        for (MediatorListener mediatorListener : collaborators) {
            mediatorListener.listenToEvent(event);
        }
    }

    public void addCollaborator(MediatorListener collaborator) {
        collaborators.add(collaborator);
    }

    public void removeCollaborator(MediatorListener collaborator) {
        collaborators.remove(collaborator);
    }
}
Mediator is a scoped bean which can register and notify collaborators. Collaborators register themself by Mediator. In Spring, a bean can implement the interface InitializingBean, so that the method afterPropertiesSet() will be called automatically after the bean's instantiation. This is similar to @PostConstruct. afterPropertiesSet() is a right place for such bean to register itself by Mediator. The bean should also implement MediatorListener in order to be notified (see listenToEvent()).
public MyBean implements MediatorListener, InitializingBean, Serializable {

    public void afterPropertiesSet() throws Exception {
        ...
        Mediator.getCurrentInstance().addCollaborator(this);
    }

    @Override
    public void listenToEvent(MediatorEvent event) {
        if (event instanceof LocaleChangeEvent) {
            // do something
        }
    }
}
We will use the same scenario with UserSettingsForm and locale changing. Beans registered by Mediator will be notified by fireEvent().
public class LocaleChangeEvent implements MediatorEvent {
    ...
}

public class UserSettingsForm implements Serializable {

    private Locale selectedLocale;
    private List<SelectItem> locales;

    public void localChangeListener(ValueChangeEvent e) {
        ...
        // notify listeners
        Mediator.getCurrentInstance().fireEvent(new LocaleChangeEvent(this, this.selectedLocale));
    }
    ...
}
Mediator pattern offers better coupling between beans, but they are still coupled with mediator. Further disadvantages: It is still necessary to register beans manually - see extra code Mediator.getCurrentInstance().addCollaborator(this). Every bean should still implement at least one MediatorListener and that brings another constraint - listenToEvent(). Every bean should implement this interface method! The probably biggest shortcoming of Mediator pattern in JSF is that it is a scoped bean. A view scoped Mediator would only work smoothly with view scoped beans. Registered view scoped beans are being removed automatically when the view scoped Mediator gets destroyed. Other scenarios can cause memory leaks or several issues. For instance, request scoped beans, registered by a view scoped Mediator, should be removed manually by calling removeCollaborator() (easy to forget). Session scoped beans should be registered by a session scoped Mediator, otherwise they will not be notified after destroying the view scoped Mediator. Etc, etc.

In the fact, the Mediator pattern is only one step better than a regular Observer / Event Listener concept. There are more flexible approaches where *any method* can catch thrown event and not only fix specified, like listenToEvent(). In the next post, we will see easy and unobtrusive ways how to catch multiply events by only one method and other advices.

Wednesday, June 20, 2012

PrimeFaces Extensions 0.5.1 released

After 9 days since the last PrimeFaces Extensions release we have done a maintenance release 0.5.1. It's built on top of PrimeFaces 3.3.1. Please see releases notes.

We fixed a couple of critical issues, especially in components AjaxExceptionHandler, KeyFilter and ImageAreaSelect. MasterDetail component got a new features - support of f:ajax / p:ajax and ability for disabled breadcrumb items. On the other hand, error-prone and slow-down features were removed. These are implicitly adding of MasterDetail Id to "process" attribute and implicitly navigation without pe:selectDetailLevel. Another update to be considered for migration is renaming of ResetEditableValues - pe:resetEditableValues was renamed to a short variant pe:resetInput.

The showcase shows new / updated components for both last releases 0.5.0 and 0.5.1. A lot of descriptions - showcase documentation - were also improved and corrected. This release is available in the Maven central repo as usually.

The work on the next release has been already started with two major re-implementations. Timeline and Layout components will be re-implemented from scratch!

Wednesday, June 13, 2012

Dynamic forms, JSF world was long waiting for

The new PrimeFaces Extensions release 0.5.0 brought a new DynaForm component. Normally, we can build a form quite straightforward by h:panelGrid oder p:panelGrid if the count of rows / columns, positions of elements, etc. are known. That's true for static forms. But it's not possible to use h:panelGrid oder p:panelGrid if a form is described dynamically, at runtime. E.g. if the entire form's definition is placed in a database or a XML file. DynaForm makes possible to build a dynamic form with labels, inputs, selects and any other elements by model. There aren't limitations. Explore all its features in the showcase. Let's show how to build a simple dynamic form.

Main steps:
Create model instance: DynaFormModel model = new DynaFormModel();
Add row to regular grid: DynaFormRow row = model.createRegularRow();
Add label: DynaFormLabel label = row.addLabel(value, colspan, rowspan);
Add editable control: DynaFormControl control = row.addControl(data, type, colspan, rowspan);
Set relationship between label and control (optional): label.setForControl(control);

Repeat four last steps as many times as needed. What is could look like from UI point of view? A screenshot after a failed form validation:


The main tag is pe:dynaForm. Child tag pe:dynaFormControl matches created in Java controls by "type" attribute. This is usually a "one to many" relation. XHTML / Java (controller bean and model) code to the dynamic form above is listed below.
<h:panelGroup id="dynaFormGroup">
    <p:messages id="messages" showSummary="true"/>

    <pe:dynaForm id="dynaForm" value="#{dynaFormController.model}" var="data">
        <pe:dynaFormControl type="input" for="txt">
            <p:inputText id="txt" value="#{data.value}" required="#{data.required}"/>
        </pe:dynaFormControl>
        <pe:dynaFormControl type="calendar" for="cal" styleClass="calendar">
            <p:calendar id="cal" value="#{data.value}" required="#{data.required}" showOn="button"/>
        </pe:dynaFormControl>
        <pe:dynaFormControl type="select" for="sel" styleClass="select">
            <p:selectOneMenu id="sel" value="#{data.value}" required="#{data.required}">
                <f:selectItems value="#{dynaFormController.languages}"/>
            </p:selectOneMenu>
        </pe:dynaFormControl>
        <pe:dynaFormControl type="textarea" for="tarea">
            <p:inputTextarea id="tarea" value="#{data.value}" required="#{data.required}" autoResize="false"/>
        </pe:dynaFormControl>
        <pe:dynaFormControl type="rating" for="rat">
            <p:rating id="rat" value="#{data.value}" required="#{data.required}"/>
        </pe:dynaFormControl>

        <f:facet name="buttonBar">
            <p:commandButton value="Submit" action="#{dynaFormController.submitForm}"
                             process="dynaForm" update="_mainForm_dynaFormGroup"/>
            <p:commandButton type="reset" value="Reset" style="margin-left: 5px;"/>
        </f:facet>
    </pe:dynaForm>
</h:panelGroup>
@ManagedBean
@ViewScoped
public class DynaFormController implements Serializable {

    private DynaFormModel model;

    private static List<SelectItem> LANGUAGES = new ArrayList<SelectItem>();

    public DynaFormController() {
        model = new DynaFormModel();

        // add rows, labels and editable controls
        // set relationship between label and editable controls to support outputLabel with "for" attribute

        // 1. row
        DynaFormRow row = model.createRegularRow();

        DynaFormLabel label11 = row.addLabel("Author", 1, 1);
        DynaFormControl control12 = row.addControl(new BookProperty("Author", true), "input", 1, 1);
        label11.setForControl(control12);

        DynaFormLabel label13 = row.addLabel("ISBN", 1, 1);
        DynaFormControl control14 = row.addControl(new BookProperty("ISBN", true), "input", 1, 1);
        label13.setForControl(control14);

        // 2. row
        row = model.createRegularRow();

        DynaFormLabel label21 = row.addLabel("Title", 1, 1);
        DynaFormControl control22 = row.addControl(new BookProperty("Title", false), "input", 3, 1);
        label21.setForControl(control22);

        // 3. row
        row = model.createRegularRow();

        DynaFormLabel label31 = row.addLabel("Publisher", 1, 1);
        DynaFormControl control32 = row.addControl(new BookProperty("Publisher", false), "input", 1, 1);
        label31.setForControl(control32);

        DynaFormLabel label33 = row.addLabel("Published on", 1, 1);
        DynaFormControl control34 = row.addControl(new BookProperty("Published on", false), "calendar", 1, 1);
        label33.setForControl(control34);

        // 4. row
        row = model.createRegularRow();

        DynaFormLabel label41 = row.addLabel("Language", 1, 1);
        DynaFormControl control42 = row.addControl(new BookProperty("Language", false), "select", 1, 1);
        label41.setForControl(control42);

        DynaFormLabel label43 = row.addLabel("Description", 1, 2);
        DynaFormControl control44 = row.addControl(new BookProperty("Description", false), "textarea", 1, 2);
        label43.setForControl(control44);

        // 5. row
        row = model.createRegularRow();

        DynaFormLabel label51 = row.addLabel("Rating", 1, 1);
        DynaFormControl control52 = row.addControl(new BookProperty("Rating", 3, true), "rating", 1, 1);
        label51.setForControl(control52);
    }

    public DynaFormModel getModel() {
        return model;
    }

    public String submitForm() {
        ... // do something
    }

    public List<SelectItem> getLanguages() {
        if (LANGUAGES.isEmpty()) {
            LANGUAGES.add(new SelectItem("en", "English"));
            LANGUAGES.add(new SelectItem("de", "German"));
            LANGUAGES.add(new SelectItem("ru", "Russian"));
            LANGUAGES.add(new SelectItem("tr", "Turkish"));
        }

        return LANGUAGES;
    }
}

public class BookProperty implements Serializable {

    private String name;
    private Object value;
    private boolean required;

    public BookProperty(String name, boolean required) {
        this.name = name;
        this.required = required;
    }

    public BookProperty(String name, Object value, boolean required) {
        this.name = name;
        this.value = value;
        this.required = required;
    }

    // getter // setter
}
You see that one of important features is a buil-in support for labels. DynaForm renders labels automatically - no need to write p:outputLabel. Another feature is autoSubmit flag. It allows pass form parameters in URL, build a form on page load and submit it automatically. More highlights: expandable extended view area (grid), open / close state saving, widget's client-side API, various facets. Next screenshots demonstrate how to build dynamic forms with lables above fields and various elements like PrimeFaces separator. Two forms in this example are switched by clicking on the "Switch model" link. Note, XHTML code with pe:dynaForm stays the same, only Java model gets changed.



Explore the corresponding code in the use case Yet another forms.

Monday, June 11, 2012

PrimeFaces Extensions 0.5.0 released

I'm pleased to announce the new release of PrimeFaces Extensions. It's fully compatible with the last PrimeFaces 3.3 release.

Please see releases notes and the showcase for new / updated components and use cases. This release is available in the Maven central repo as usually. See also recently updated Getting Started for more details.

We plan to deploy next releases in the cloud only. The showcase was already tested and successfully deployed on OpenShift. OpenShift provides a free JBoss 7 instance with JSF Mojarra implementation. It's not possible to change JSF impl. in the current JBoss version, so that we will stick first with Mojarra. Internal tests are running against MyFaces too (there are actually a lot of differences in both JSF impl.).

Another news: in the next 4-5 months I will do less than ever activities in the PrimeFaces Extensions. I would like to concentrate fully on the upcoming PrimeFaces Cookbook. Packt publisher has very tight deadlines for chapter's delivery dates. However, I hope we can offer interesting features in the next release iteration.

Wednesday, June 6, 2012

PrimeFaces Extensions is getting solid. Trailer.

PrimeFaces Extensions project is going to be released on 9. June (Saturday). Here is a video trailer to the upcoming version. The official, detailed announcement will be published after the 0.5.0 release.
Stay tuned.