Thursday, December 27, 2012

GET / POST with RESTful Client API

There are many stuff in the internet how to work with RESTful Client API. These are basics. But even though the subject seems to be trivial, there are hurdles, especially for beginners. In this post I will try to summurize my know-how how I did this in real projects. I usually use Jersey (reference implementation for building RESTful services). See e.g. my other post. In this post, I will call a real remote service from JSF beans. Let's write a session scoped bean RestClient.
package com.cc.metadata.jsf.controller.common;

import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.WebResource;

import java.io.Serializable;
import javax.annotation.PostConstruct;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
import javax.faces.context.FacesContext;

/**
 * This class encapsulates some basic REST client API.
 */
@ManagedBean
@SessionScoped
public class RestClient implements Serializable {

    private transient Client client;

    public String SERVICE_BASE_URI;

    @PostConstruct
    protected void initialize() {
        FacesContext fc = FacesContext.getCurrentInstance();
        SERVICE_BASE_URI = fc.getExternalContext().getInitParameter("metadata.serviceBaseURI");

        client = Client.create();
    }

    public WebResource getWebResource(String relativeUrl) {
        if (client == null) {
            initialize();
        }

        return client.resource(SERVICE_BASE_URI + relativeUrl);
    }

    public ClientResponse clientGetResponse(String relativeUrl) {
        WebResource webResource = client.resource(SERVICE_BASE_URI + relativeUrl);
        return webResource.accept("application/json").get(ClientResponse.class);
    }
}
In this class we got the service base URI which is specified (configured) in the web.xml.
<context-param>
   <param-name>metadata.serviceBaseURI</param-name>
   <param-value>http://somehost/metadata/</param-value>
</context-param>
Furthermore, we wrote two methods to receive remote resources. We intend to receive resources in JSON format and convert them to Java objects. The next bean demonstrates how to do this task for GET requests. The bean HistoryBean converts received JSON to a Document object by using GsonConverter. The last two classes will not be shown here (they don't matter). Document is a simple POJO and GsonConverter is a singleton instance which wraps Gson.
package com.cc.metadata.jsf.controller.history;

import com.cc.metadata.jsf.controller.common.RestClient;
import com.cc.metadata.jsf.util.GsonConverter;
import com.cc.metadata.model.Document;

import com.sun.jersey.api.client.ClientResponse;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ManagedProperty;
import javax.faces.bean.ViewScoped;

/**
 * Bean getting history of the last extracted documents.
 */
@ManagedBean
@ViewScoped
public class HistoryBean implements Serializable {

    @ManagedProperty(value = "#{restClient}")
    private RestClient restClient;

    private List<Document> documents;
    private String jsonHistory;

    public List<Document> getDocuments() {
        if (documents != null) {
            return documents;
        }

        ClientResponse response = restClient.clientGetResponse("history");

        if (response.getStatus() != 200) {
            throw new RuntimeException("Failed service call: HTTP error code : " + response.getStatus());
        }

        // get history as JSON
        jsonHistory = response.getEntity(String.class);

        // convert to Java array / list of Document instances
        Document[] docs = GsonConverter.getGson().fromJson(jsonHistory, Document[].class);
        documents = Arrays.asList(docs);

        return documents;
    }

    // getter / setter
 ...
}
The next bean demonstrates how to communicate with the remote service via POST. We intent to send the content of uploaded file. I use the PrimeFaces' FileUpload component, so that the content can be extracted as InputStream from the listener's parameter FileUploadEvent. This is not important here, you can also use any other web frameworks to get the file content (also as byte array). More important is to see how to deal with RESTful Client classes FormDataMultiPart and FormDataBodyPart.
package com.cc.metadata.jsf.controller.extract;

import com.cc.metadata.jsf.controller.common.RestClient;
import com.cc.metadata.jsf.util.GsonConverter;
import com.cc.metadata.model.Document;

import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.WebResource;
import com.sun.jersey.core.header.FormDataContentDisposition;
import com.sun.jersey.multipart.FormDataBodyPart;
import com.sun.jersey.multipart.FormDataMultiPart;

import org.primefaces.event.FileUploadEvent;

import java.io.IOException;
import java.io.Serializable;
import javax.faces.application.FacesMessage;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ManagedProperty;
import javax.faces.bean.ViewScoped;
import javax.faces.context.FacesContext;

import javax.ws.rs.core.MediaType;

/**
 * Bean for extracting document properties (metadata).
 */
@ManagedBean
@ViewScoped
public class ExtractBean implements Serializable {

    @ManagedProperty(value = "#{restClient}")
    private RestClient restClient;

    private String path;

    public void handleFileUpload(FileUploadEvent event) throws IOException {
        String fileName = event.getFile().getFileName();

        FormDataMultiPart fdmp = new FormDataMultiPart();
        FormDataBodyPart fdbp = new FormDataBodyPart(FormDataContentDisposition.name("file").fileName(fileName).build(),
                event.getFile().getInputstream(), MediaType.APPLICATION_OCTET_STREAM_TYPE);
        fdmp.bodyPart(fdbp);

        WebResource resource = restClient.getWebResource("extract");
        ClientResponse response = resource.accept("application/json").type(MediaType.MULTIPART_FORM_DATA).post(
                ClientResponse.class, fdmp);

        if (response.getStatus() != 200) {
            throw new RuntimeException("Failed service call: HTTP error code : " + response.getStatus());
        }

        // get extracted document as JSON
        String jsonExtract = response.getEntity(String.class);

        // convert to Document instance
        Document doc = GsonConverter.getGson().fromJson(jsonExtract, Document.class);

        ...
    }

    // getter / setter
 ...
}
Last but not least, I would like to demonstrate how to send a GET request with any query string (URL parameters). The next method asks the remote service by URL which looks as http://somehost/metadata/extract?file=<some file path>
public void extractFile() {
 WebResource resource = restClient.getWebResource("extract");
 ClientResponse response = resource.queryParam("file", path).accept("application/json").get(
   ClientResponse.class);

 if (response.getStatus() != 200) {
  throw new RuntimeException("Failed service call: HTTP error code : " + response.getStatus());
 }

 // get extracted document as JSON
 String jsonExtract = response.getEntity(String.class);

 // convert to Document instance
 Document doc = GsonConverter.getGson().fromJson(jsonExtract, Document.class);

 ...
}

Tuesday, December 18, 2012

Elegant truncated text with ellipses in web

Sometimes text is big than allowed space. Assume you have a table column with a very long text and don't want to have line breaks or make the table bigger in order to show the text completely. Having a fluid truncated (clipped) text with three dots at the end would be a good option. It would be nice to show a truncated text with ... and put a tooltip which displays the whole text on mouseover. Furthemore, the text should display more content or less content dynamically when resizing browser and the available space gets bigger or shrunk. How to deal with fluid truncated text? Well, there are some JavaScript solutions with advanced configuration. One of them is e.g. dotdotdot and the another one is trunk8

But in simple cases we don't need JavaScript at all. There is a CSS property text-overflow: ellipsis which works fine on container elements (like div) with white-space: nowrap and overflow: hidden. I defined a style class truncate which can be applied to any container element.
.truncate {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    -o-text-overflow: ellipsis;
    -ms-text-overflow: ellipsis;
    display: block;
    position: absolute;
}
This works smoothly. Example:
<div class="truncate">
    My very very very very very very very very very long text
</div>
The text will fit the space of the div element now.

As I develop JSF applications I need it for text in PrimeFaces p:column (rendered as td element). For that we need to set a max. width on p:column. For my project I decided to restrict the max. width to 160px. Style classes applied to p:column and the inner div (containing the text) look as follows:
.truncate {
    max-width: 160px;
    width: 160 px\9;
}

.truncate > div {
    width: 160 px\9;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    -o-text-overflow: ellipsis;
    -ms-text-overflow: ellipsis;
    display: block;
    position: absolute;
}
\9 is a hack for IE7 / IE8. Unfortunately, I could not do text in table's columns fluid for IE7 / E8, so that I set a fix width 160px. The code in XHTML is like this one:
<p:column headerText="Description" sortBy="#{user.description}" styleClass="truncate">
    <h:outputText id="desc" value="#{user.description}"/>
    <pe:tooltip for="desc" value="#{user.description}"/>
</p:column>
And a final picture to complete this blog post.


The visible text's content (in the column "Description") is adjusted automatically while the table changes its size. There are no line breaks in columns when some text doesn't fit the available column's space. The table looks compacted.

Sunday, December 16, 2012

PrimeFaces Extensions 0.6.2 released

We are glad to announce the new release 0.6.2 of PrimeFaces Extensions. This is a maintenance release. It contains fixes and additional features for CKEditor, CodeMirror, Timeline, Tooltip and Layout. Layout options as tag attributes are back! Now you can build layout either by model (LayoutOptions class) or direct by attributes of pe:layoutPane. There are also new EL functions (check the showcase) and a nice extension for the JsonConverter (can handle Generic types). We have also released Resource Optimizer Maven plugin with updated dependencies to Google Closure Compiler and Plexus Utils.

Some quick links to dive into PrimeFaces Extensions:

Getting Started
Showcase Mojarra
Showcase MyFaces

All releases are available in the Maven central repo as usally. The next release will be again a maintenance release 0.6.3. The main focus is a migration to GitHub. After that we will add new components in the 0.7.0 release.

Have fun!

Thursday, December 6, 2012

Register hotkeys for PrimeFaces dialogs

In this blog post I would like to share a jQuery based (low level), fast working solution to register hotkeys for PrimeFaces dialogs. Why do we need this low level solution? Primary because the PrimeFaces component p:hotkey doesn't work as expected in dialogs. We were only interested in two hotkeys ESC and ENTER, so that I will only show a solution for these two hotkeys.

Requirements: When user presses the ESC key, a dialog should be closed. When user presses the ENTER key, the dialog action should be submitted (imagine e.g. a "Are you sure you want to delete?" dialog). Dialog can look like as follows. In the real world it is better to please the dialog into a composite component in order to reuse it.
<p:confirmDialog header="Confirmation" widgetVar="confirmDlgWidget" appendToBody="true">
  <f:facet name="message">
     ...
  </f:facet>
  <h:form>
    <p:commandButton value="OK" process="@this" update="..." action="..." oncomplete="confirmDlgWidget.hide();"/>
    <p:commandButton value="Cancel" onclick="confirmDlgWidget.hide();" type="button"/>
  </h:form>
</p:confirmDialog>
After the dialog tag, the following short JS script should be placed.
<script type="text/javascript">
 /* <![CDATA[ */
 $(function () {
    registerConfirmDialogHotkeys(confirmDlgWidget);
 });
 /* ]]> */
</script>
The JS function itself is located in a file. Here how it was implemented:
function registerConfirmDialogHotkeys(dlgWidget) {

    var key = 'keydown.pfdialog_' + dlgWidget.id;

    $(document).off(key).on(key, function (e) {
        var keyCode = $.ui.keyCode;
        var active = parseInt(dlgWidget.jq.css('z-index')) === PrimeFaces.zindex;

        if (dlgWidget.jq.hasClass('ui-overlay-visible') && active) {
            if (e.which === keyCode.ESCAPE) {
                dlgWidget.hide();
            } else if (e.which === keyCode.ENTER || e.which == keyCode.NUMPAD_ENTER) {
                dlgWidget.jq.find('button:submit:first').click();
                e.preventDefault();
            }
        }
    });
}
The function registers a namespaced keydown JS callback on document. The callback checks if the dialog is active currently. This is normally a last opened dialog. If it is active and the user has pressed ESC, then hide the dialog (widget variable is passed into the function). If it is active and the user has pressed ENTER, then find the first submit button within this dialog and click it to cause an action execution. That's all.

Tuesday, November 27, 2012

Validate unique input values within DataTable column

This post is an answer for BalusC to our discussion in OmniFaces' issue tracker. The goal is to show how to validate values in an editable DataTable if they are unique within a column or not. I will not write too much explanation. I will bomb readers with the code :-). The code is written for PrimeFaces DataTable, but it is easy to adjust it to the JSF standard DataTable or any other data iteration components like DataList in PrimeFaces. Validation of unique input values supports lazy loaded tables and dynamic columns as well. First, we will write a tag handler ValidateUniqueColumn (how to register it in a tag lib. is not a subject of this post). ValidateUniqueColumn has to be attached to the entire p:dataTable and have two attributes: int index (index of the column to be validated) and boolean flag skipEmpty (if empty values should be skipped when validating; default is true). XHTML snippet as using example:
<p:dataTable ...>
    <p:columns ...>
        ...
    </p:columns>
    ...
    <p:column>
        ...
    </p:column>

    <xyz:validateUniqueColumn index="4" skipEmpty="false"/>
    <xyz:validateUniqueColumn index="#{bean.columnIndexToValidate}"/>
</p:dataTable>
Implementation of the tag handler:
public class ValidateUniqueColumn extends TagHandler {

    public static final String UNSUBSCRIBE_PRERENDER_LISTENERS = "unsubscribePreRenderListeners";

    private final TagAttribute index;
    private final TagAttribute skipEmpty;

    public ValidateUniqueColumn(TagConfig config) {
        super(config);
        this.index = getRequiredAttribute("index");
        this.skipEmpty = getAttribute("skipEmpty");
    }

    @Override
    public void apply(FaceletContext ctx, UIComponent parent) throws IOException {
        if (!ComponentHandler.isNew(parent)) {
            return;
        }

        Object objIndex;

        if (index.isLiteral()) {
            // literal
            objIndex = index.getValue();
        } else {
            // value expression
            objIndex = index.getValueExpression(ctx, int.class);
        }

        Object objSkipEmpty;

        if (skipEmpty == null) {
            objSkipEmpty = true;
        } else if (skipEmpty.isLiteral()) {
            // literal
            objSkipEmpty = skipEmpty.getValue();
        } else {
            // value expression
            objSkipEmpty = skipEmpty.getValueExpression(ctx, boolean.class);
        }

        // register a PreRender listener
        parent.subscribeToEvent(PreRenderComponentEvent.class, new PreRenderTableListener(objIndex, objSkipEmpty));
        // set a flag that all before registered PreRenderTableListener instances must be unsubscribed
        parent.getAttributes().put(UNSUBSCRIBE_PRERENDER_LISTENERS, true);
    }
}
Implementation of PreRenderTableListener:
public class PreRenderTableListener implements ComponentSystemEventListener, Serializable {

    private static final long serialVersionUID = 20111114L;
    private Logger LOG = Logger.getLogger(PreRenderTableListener.class);

    private Object index;
    private Object skipEmpty;

    /**
     * This constructor is required for serialization.
     */
    public PreRenderTableListener() {
    }

    public PreRenderTableListener(Object index, Object skipEmpty) {
        this.index = index;
        this.skipEmpty = skipEmpty;
    }

    @Override
    public void processEvent(ComponentSystemEvent event) {
        UIComponent source = event.getComponent();
        if (!source.isRendered()) {
            return;
        }

        DataTable dataTable;
        if (source instanceof DataTable) {
            dataTable = (DataTable) source;
        } else {
            LOG.warn("Validator ValidateUniqueColumn can be only applied to PrimeFaces DataTable");
            return;
        }

        if (index == null) {
            LOG.warn("Column index of the Validator ValidateUniqueColumn is null");
            return;
        }

        Boolean deleteListeners = (Boolean) source.getAttributes().get(
                ValidateUniqueColumn.UNSUBSCRIBE_PRERENDER_LISTENERS);
        if ((deleteListeners != null) && deleteListeners) {
            // unsubscribe all listeners only once - important for AJAX updates
            source.getAttributes().remove(ValidateUniqueColumn.UNSUBSCRIBE_PRERENDER_LISTENERS);

            Iterator<PostValidateTableListener> iter = getPostValidateTableListeners(dataTable).iterator();
            while (iter.hasNext()) {
                dataTable.unsubscribeFromEvent(PostValidateEvent.class, iter.next());
            }
        }

        int columnIndex;
        if (index instanceof ValueExpression) {
            // value expression
            Object obj = ((ValueExpression) index).getValue(FacesContext.getCurrentInstance().getELContext());
            columnIndex = Integer.valueOf(obj.toString());
        } else {
            // literal
            columnIndex = Integer.valueOf(index.toString());
        }

        boolean skipEmptyValue;
        if (skipEmpty instanceof ValueExpression) {
            // value expression
            Object obj = ((ValueExpression) skipEmpty).getValue(FacesContext.getCurrentInstance().getELContext());
            skipEmptyValue = Boolean.valueOf(obj.toString());
        } else {
            // literal
            skipEmptyValue = Boolean.valueOf(skipEmpty.toString());
        }

        PostValidateTableListener pvtListener = new PostValidateTableListener(columnIndex, skipEmptyValue);
        dataTable.subscribeToEvent(PostValidateEvent.class, pvtListener);
    }

    protected List<PostValidateTableListener> getPostValidateTableListeners(UIComponent component) {
        List<PostValidateTableListener> postValidateTableListeners = new ArrayList<PostValidateTableListener>();

        List<SystemEventListener> systemEventListeners = component.getListenersForEventClass(PostValidateEvent.class);
        if ((systemEventListeners != null) && !systemEventListeners.isEmpty()) {
            for (SystemEventListener systemEventListener : systemEventListeners) {
                if (systemEventListener instanceof PostValidateTableListener) {
                    postValidateTableListeners.add((PostValidateTableListener) systemEventListener);
                }

                FacesListener wrapped = null;
                if (systemEventListener instanceof FacesWrapper<?>) {
                    wrapped = (FacesListener) ((FacesWrapper<?>) systemEventListener).getWrapped();
                }

                while (wrapped != null) {
                    if (wrapped instanceof PostValidateTableListener) {
                        postValidateTableListeners.add((PostValidateTableListener) wrapped);
                    }

                    if (wrapped instanceof FacesWrapper<?>) {
                        wrapped = (FacesListener) ((FacesWrapper<?>) wrapped).getWrapped();
                    } else {
                        wrapped = null;
                    }
                }
            }
        }

        return postValidateTableListeners;
    }
}
Implementation of PostValidateTableListener:
public class PostValidateTableListener implements ComponentSystemEventListener, Serializable {

    private static final long serialVersionUID = 20111114L;
    private static final Set<VisitHint> VISIT_HINTS = EnumSet.of(VisitHint.SKIP_UNRENDERED);

    private int index = -1;
    private boolean skipEmpty;

    /**
     * This constructor is required for serialization.
     */
    public PostValidateTableListener() {
    }

    public PostValidateTableListener(int index, boolean skipEmpty) {
        this.index = index;
        this.skipEmpty = skipEmpty;
    }

    public int getIndex() {
        return index;
    }

    @Override
    public void processEvent(ComponentSystemEvent event) {
        UIComponent source = event.getComponent();
        if (!source.isRendered() || (index == -1)) {
            return;
        }

        FacesContext fc = FacesContext.getCurrentInstance();
        Map<String, String> requestParamMap = fc.getExternalContext().getRequestParameterMap();

        // buffer unique input values during iteration in a list
        List<Object> columnValues = new ArrayList<Object>();

        DataTable dataTable = (DataTable) source;
        int first = dataTable.getFirst();
        int rowCount = dataTable.getRowCount();
        int rows = dataTable.getRows();

        if (dataTable.isLazy()) {
            for (int rowIndex = 0; rowIndex < rowCount; rowIndex++) {
                if ((rowIndex % rows) == 0) {
                    dataTable.setFirst(rowIndex);
                    dataTable.loadLazyData();
                }

                // get next value of the first editable component in the specified column
                Object value = getColumnValue(fc, requestParamMap, dataTable, rowIndex);

                // compare with last stored unique values
                if (isUnique(fc, columnValues, value)) {
                    columnValues.add(value);
                } else {
                    break;
                }
            }

            //restore
            dataTable.setFirst(first);
            dataTable.loadLazyData();
        } else {
            for (int rowIndex = 0; rowIndex < rowCount; rowIndex++) {
                // get next value of the first editable component in the specified column
                Object value = getColumnValue(fc, requestParamMap, dataTable, rowIndex);

                // compare with last stored unique values
                if (isUnique(fc, columnValues, value)) {
                    columnValues.add(value);
                } else {
                    break;
                }
            }

            //restore
            dataTable.setFirst(first);
        }
    }

    private Object getColumnValue(FacesContext fc, Map<String, String> requestParamMap,
     DataTable dataTable, int rowIndex) {
        dataTable.setRowIndex(rowIndex);

        if (!dataTable.isRowAvailable()) {
            return null;
        }

        List<UIColumn> columns = dataTable.getColumns();
        if (index < columns.size()) {
            int i = -1;
            UIColumn foundColumn = null;

            for (UIColumn col : columns) {
                if (col.isRendered()) {
                    i++;
                }

                if (index == i) {
                    foundColumn = col;
                    break;
                }
            }

            if (foundColumn == null) {
                // column for given index was not found
                return null;
            }

            if (foundColumn instanceof DynamicColumn) {
                ((DynamicColumn) foundColumn).applyModel();
            }

            List<UIComponent> children = foundColumn.getChildren();
            for (UIComponent component : children) {
                // find the first editable rendered component
                FirstInputVisitCallback visitCallback = new FirstInputVisitCallback();
                component.visitTree(VisitContext.createVisitContext(fc, null, VISIT_HINTS), visitCallback);

                EditableValueHolder editableValueHolder = visitCallback.getEditableValueHolder();
                if (editableValueHolder != null) {
                    String clientId = ((UIComponent) editableValueHolder).getClientId(fc);
                    String value = requestParamMap.get(clientId);

                    // return converted value for comparison
                    return ComponentUtils.getConvertedValue(fc, editableValueHolder, value);
                }
            }
        }

        return null;
    }

    private boolean isUnique(FacesContext fc, List<Object> columnValues, Object value) {
        if (skipEmpty && ((value == null) || (value.toString().length() < 1))) {
            return true;
        }

        for (Object columnValue : columnValues) {
      // compare values with EqualsBuilder from Apache commons project
            if (new EqualsBuilder().append(columnValue, value).isEquals()) {
                // not unique
                fc.addMessage(null, MessageUtils.getMessage("msg_tableNotUniqueValues", index + 1));
                fc.validationFailed();
                fc.renderResponse();
                return false;
            }
        }

        return true;
    }
}
How to get converted value from the submitted one is not shown here.

Thursday, November 1, 2012

Announcement: PrimeFaces Cookbook will be available soon

I'm glad to tell the PrimeFaces Community that the Packt Publisher published an official announcement to the first PrimeFaces book - PrimeFaces Cookbook. The book will give quick solutions to common and advanced use cases. A current table of contents is available on GitHub. As you can see the book size is ca. 410 pages and the release date is February 2013. The book is going to run through the review process now. It will be possible soon to order the book in advance on Amazon, Safari Books Online and other stores.

I would like to thanks all people who accompanied me and my co-writer Mert during the entire writing process with support and suggestions. Thanks!

Saturday, October 13, 2012

PrimeFaces Extensions 0.6.1 released

We are glad to announce a new maintenance release of the PrimeFaces Extensions 0.6.1. The main focus was on fixing critical issues. The next release will be also a maintenance release with some improvements / new features for existing components. Our resource optimizer plugin will be upgraded as well. After that you can expect more from this project because I will finish the writing of the "PrimeFaces Cookbook" and we plan to add new components again.

Some quick links to dive into PrimeFaces Extensions:
Getting Started
Showcase Mojarra
Showcase MyFaces

Have fun!

Sunday, October 7, 2012

"Copy to Clipboard" feature in web

Sometimes we need a "Copy to Clipboard" feature to copy some content from an input field or textarea into the OS clipboard. How to implement this feature in JavaScript? There are a lot of articles with this topic in the web. They say - there is only one way to do this cross-browser. This way is Flash. You can read this article or this one and see that a special Flash object is required. A general problem is that installing Flash in browser breaks the paradigm of plain web applications. There is no guarantee that a next Flash update will not break the "Copy to Clipboard" functionality. Also be aware of security restriction by Adobe Flash Player - it doesn’t work at local computer at all.

What is an alternative way? After reading some posts I came to the thought to select the text to be copied automatically and show a tooltip with a little help. The next example demonstrates this idea in JSF with PrimeFaces components. Assume, you have a button "Copy to Clipboard" and an input field with a link (URL) to be copied. Click on the button should call an JavaScript function, say copyInputToClipboard(). As tooltip I will use pe:tooltip from the PrimeFaces Extensions project. The tooltip has showEvent="false" and doesn't get shown by any event. We will show it programmatically by calling widget's method show().
...

<p:commandButton icon="ui-icon ui-icon-copy" type="button"
 value="Copy to Clipboard"
 title="Copy to Clipboard"
 onclick="copyInputToClipboard('#{cc.clientId}:textUrl','widget_#{cc.clientId.replaceAll('-|:','_')}_tip')"/>

...

<p:inputText id="textUrl" value="#{...}" maxlength="2047" autocomplete="off"/>

<pe:tooltip for="textUrl" widgetVar="widget_#{cc.clientId.replaceAll('-|:','_')}_tip"
     value="Press Ctrl+C to copy this link into the clipboard"
     myPosition="top center" atPosition="bottom center" showEvent="false" hideEvent="blur"/>

...
All components are placed within a composite component. This is why I used cc.clientId. The function copyInputToClipboard() expects two parameters: clientId of the input field and the widget variable of the tooltip. In Internet Explorer we can use the object window.clipboardData to achieve the desired functionality. Just call window.clipboardData.setData('text', inputValue) with some input value and the task is done. In other browsers, the best approach would be to select the input text on button click and to show a tooltip with text "Press Ctrl+C to copy this link into the clipboard".
function copyInputToClipboard(inputId, widgetVarTip) {
    if (window.clipboardData && clipboardData.setData) {
        // IE
        var inputValue = $(PrimeFaces.escapeClientId(inputId)).val();
        window.clipboardData.setData('text', inputValue);
    } else {
        // other browsers
        var el = $(PrimeFaces.escapeClientId(inputId));
        el.focus();
        el.select();
        if (widgetVarTip && window[widgetVarTip]) {
            window[widgetVarTip].hide();
            window[widgetVarTip].show();
        }
    }
}
Note that tooltip disappears on blur. The picture demonstrates my implementation.


I hope you have enjoyed this small excursion into the "Copy to Clipboard" feature.

Monday, October 1, 2012

PrimeFaces Extensions 0.6.0 released

We are proud to announce the new release of the PrimeFaces Extensions 0.6.0. It is built on top of PrimeFaces 3.4.1. Please see releases notes.

Main subjects and key features in this release:

  • A lot of bugxifes and improvements / new features in Tooltip, MasterDetail, CKEditor, CodeMirror, InputNumber, AjaxExceptionHandler, ImportConstants, TriStateCheckbox, TriStateManyCheckbox.
  • New component Waypoint which makes a solid base for modern UI patterns that depend on a user's scroll position on a page.
  • New component Switch, regular component to counterparts c:choose, c:when, c:otherwise.
  • BlockPanel was renamed to Spotlight. Spotlight allows you to restrict input to a particular element by masking all other page content.
  • AjaxStatus and Paginator components were removed. PrimeFaces core has the same functionality now.
  • More utilities, like a new EL function to escape jQuery selectors in facelets.
  • Layout component was reimplemented. Layout options are created by model now. You can combine several options in Java like you would build a Tree by Tree nodes. By this way, all possible options are supported. This is only a first step, we didn't add some features like updatable nested (child) layouts. Also state management only works for direct layout panes at the moment due to some issues in current native jQuery Layout plugin (will be fixed soon).
  • Timeline component was reimplemented and improved. This is the fastest Timeline implementation in the world :-) with native JavaScript code in its core parts (without jQuery)! There are a lot of features and about 25 use cases. We have implemented 5. Ask us if you have any questions. Here a screenshot yet (click to enlarge).


This release is available in the Maven central repo as usually.

Sunday, September 16, 2012

Configure timeout for CDI conversations

CDI conversation scope is a nice feature when developing JSF applications. Imagine you have large data tables which take a long time to be loaded. You normally don't want to place the loaded data in session scoped beans by reason of high memory consumption. And you can't place the loaded data in view scoped beans because you wouldn't like always to reload data again if user leave and enter the same view. It would be nice only to keep data if user enters the same page within a certain time interval and reload them again if the bean was not accessed within this time interval. This can be achieved by conversation scoped bean with timeout. We will deal with MyFaces CODI (CDI Extensions) and see how to set a custom timeout for beans annotated with @ConversationScoped. The default timeout is 30 min. what is too long for our example. We will configure it for 1 min. The first step is to extend CODI's ConversationConfig and overwrite the method getConversationTimeoutInMinutes(). Let's write a class AlternativeConversationConfig.
package controller.cdi;

import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.inject.Alternative;
import javax.enterprise.inject.Specializes;
import org.apache.myfaces.extensions.cdi.core.api.config.ConfigEntry;
import org.apache.myfaces.extensions.cdi.core.api.scope.conversation.config.ConversationConfig;

@ApplicationScoped
@Alternative
@Specializes
public class AlternativeConversationConfig extends ConversationConfig {

    @ConfigEntry
    public int getConversationTimeoutInMinutes() {
        return 1;
    }
}
The important thing is the annotation @Specializes which allows to inject AlternativeConversationConfig instead of ConversationConfig at every existing places. The second step is a proper entry in beans.xml to use (activate) our class on all injection points for ConversationConfig.
<alternatives>
    <class>controller.cdi.AlternativeConversationConfig</class>
</alternatives>
Server's log output during startup contains these lines now
config implementation:
controller.cdi.AlternativeConversationConfig$Proxy$_$$_WeldClientProxy
config implementation: controller.cdi.AlternativeConversationConfig
   method: getConversationTimeoutInMinutes
   value: 1
To check if everything is ok, we can write a conversation scoped bean and use it in facelets.
import java.io.Serializable;
import javax.faces.event.ActionEvent;
import javax.inject.Named;
import org.apache.myfaces.extensions.cdi.core.api.scope.conversation.ConversationScoped;

@Named
@ConversationScoped
public class CdiBeanConversationScoped implements Serializable {
 
    private int counter;
 
    public int getCounter() {
        return counter;
    }
 
    public void increment(ActionEvent e) {
        counter++;
    }
}
<h:outputText id="counter" value="Conversation scoped counter: #{cdiBeanConversationScoped.counter}"/>
   
<p:commandButton value="Increment counter" process="@this" update="counter"
                                         actionListener="#{cdiBeanConversationScoped.increment}"/>
The counter will expire after 1 min. if no access to the bean happens within this time interval. Simple push the button to increment the counter, wait longer than 1 min. and increment it again. You will see that counter was reseted.

Sunday, August 5, 2012

Bundled PrimeFaces Themes

I have created a sub-project in PrimeFaces Extensions for everybody who would not like to confront with adding multiply theme JAR files and only needs one bundled JAR with all PrimeFaces themes. One JAR file would also reduce the scanning time during startup (JSF, CDI, ... look for marker XML files) in comparison to 35+ separate files.

Last release of the bundled themes can be found in the Maven Central repository. The release version is synchronized with the current release of PrimeFaces themes. Add this dependency to your pom.xml and you are done.
<dependencies>
    ...    
    <dependency>
        <groupId>org.primefaces.extensions</groupId>
        <artifactId>all-themes</artifactId>
        <version>1.0.6</version>
    </dependency>
    ...
</dependencies>
Non Maven users can download the JAR file direct from the Maven Central repository.

You can consider this as an "all-in-one" themes add-on. We don't modify themes, we only collect them by means of the Maven Assembly Plugin. A simple Maven project aggregates PrimeFaces themes and builds a single JAR file with all available themes. Here is a hint how it works. The Maven project with packaging pom is configured as follows:
<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-assembly-plugin</artifactId>
            <configuration>
                <appendAssemblyId>false</appendAssemblyId>
            </configuration>
            <executions>
                <execution>
                    <id>package-all</id>
                    <phase>package</phase>
                    <goals>
                        <goal>single</goal>
                    </goals>
                    <configuration>
                        <descriptors>
                            <descriptor>src/main/assembly/all-themes.xml</descriptor>
                        </descriptors>
                    </configuration>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

<dependencies>
    <dependency>
        <groupId>org.primefaces.themes</groupId>
        <artifactId>afterdark</artifactId>
        <version>${primefaces.theme.version}</version>
        <scope>runtime</scope>        
    </dependency>
    ...
    <dependency>
        <groupId>org.primefaces.themes</groupId>
        <artifactId>vader</artifactId>
        <version>${primefaces.theme.version}</version>
        <scope>runtime</scope>        
    </dependency>
</dependencies>

<properties>
    <primefaces.theme.version>1.0.6</primefaces.theme.version>    
</properties>
The Maven Assembly Plugin takes all dependencies and re-packs them as a single JAR. Repacking instructions are defined in the assembly descriptor all-themes.xml.
<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="...">
    <id>all-themes</id>
    <formats>
        <format>jar</format>
    </formats>

    <includeBaseDirectory>false</includeBaseDirectory>

    <dependencySets>
        <dependencySet>
            <unpack>true</unpack>
            <useProjectArtifact>false</useProjectArtifact>
            <useTransitiveDependencies>false</useTransitiveDependencies>
        </dependencySet>
    </dependencySets>
</assembly>
That's all.

Friday, July 27, 2012

Google Chrome Extension for PrimeFaces showcase

I've created a Google Chrome Extension with quick links to the components in the last PrimeFaces Showcase. Links are sorted alphabetically. Everybody who likes an alphabetic order of PrimeFaces components can download the file pfchromeextension.crx from here. Simple drag and drop this file into the browser window and confirm the installation. You will see a small clickable "PrimeFaces icon" on the top right corner. I've also corrected some link names in comparison to original ones. Enjoy!


I was also thinking about two links for each component - one for showcase and one for the corresponding page in the User's Guide (PDF). For instance, Accordion (demo | pdf). As far as I know, Google Chrome has a built-in PDF reader and can open PDF on every page you specify in URL. What do you think?

Thursday, July 26, 2012

Merging with Subversion and IntelliJ IDEA

A common task - merge software changes from trunk with branch and vice versa - is an easy task with IntelliJ IDEA. There are a couple of ways how to do this in IDEA. Recently, I found the easiest way in my opinion. Assume, we would like to merge changes in trunk with branch.

1) Check out the branch as a working copy.
2) Go to the menu "VCS" --> "Intergrate Project".
3) Select the checked out branch project on the left side.
4) On the right side, select branch URL and revision number, where you want to merge to, for the "Source 1".
5) On the right side, select trunk URL (revision HEAD), where you want to merge from, for the "Source 2".
6) Uncheck "Use ancestry" and click the "OK" button.

 
You're done. Review made changes in the "Changes" view and commit them. Happy merging!

Saturday, July 21, 2012

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

In the last post, we learnt event-based communication on basis of Observer / Event Listener and Mediator patterns. Due to their shortcomings I would like to show more efficient ways for event-based communication. We will start with Google Guava EventBus and end up with CDI (Contexts and Dependency Injection for the Java EE platform).

Guava EventBus
Google Guava library has an useful package eventbus. The class EventBus allows publish-subscribe-style communication between components without requiring the components to explicitly register with one another. Because we develop web applications, we should encapsulate an instance of this class in a scoped bean. Let's write EventBusProvider bean.
public class EventBusProvider implements Serializable {

    private EventBus eventBus = new EventBus("scopedEventBus");

    public static EventBus getEventBus() {
        // access EventBusProvider bean
        ELContext elContext = FacesContext.getCurrentInstance().getELContext();
        EventBusProvider eventBusProvider =
            (EventBusProvider) elContext.getELResolver().getValue(elContext, null, "eventBusProvider");

        return eventBusProvider.eventBus;
    }
}
I would like to demonstrate all main features of Guava EventBus in only one example. Let's write the following event hierarchy:
public class SettingsChangeEvent {

}

public class LocaleChangeEvent extends SettingsChangeEvent {

    public LocaleChangeEvent(Object newLocale) {
        ...
    }
}

public class TimeZoneChangeEvent extends SettingsChangeEvent {

    public TimeZoneChangeEvent(Object newTimeZone) {
        ...
    }
}
The next steps are straightforward. To receive events, an object (bean) should expose a public method, annotated with @Subscribe annotation, which accepts a single argument with desired event type. The object needs to pass itself to the register() method of EventBus instance. Let's create two beans:
public MyBean1 implements Serializable {

    @PostConstruct
    public void initialize() throws Exception {
        EventBusProvider.getEventBus().register(this);
    }

    @Subscribe
    public void handleLocaleChange(LocaleChangeEvent event) {
        // do something
    }

    @Subscribe
    public void handleTimeZoneChange(TimeZoneChangeEvent event) {
        // do something
    }
}

public MyBean2 implements Serializable {

    @PostConstruct
    public void initialize() throws Exception {
        EventBusProvider.getEventBus().register(this);
    }

    @Subscribe
    public void handleSettingsChange(SettingsChangeEvent event) {
        // do something
    }
}
To post an event, simple provide the event object to the post() method of EventBus instance. The EventBus instance will determine the type of event and route it to all registered listeners.
public class UserSettingsForm implements Serializable {

    private boolean changed;

    public void localeChangeListener(ValueChangeEvent e) {
        changed = true;        
 
        // notify subscribers
        EventBusProvider.getEventBus().post(new LocaleChangeEvent(e.getNewValue()));
    }

    public void timeZoneChangeListener(ValueChangeEvent e) {
        changed = true;        
 
        // notify subscribers
        EventBusProvider.getEventBus().post(new TimeZoneChangeEvent(e.getNewValue()));
    }

    public String saveUserSettings() {
        ...

        if (changed) {
            // notify subscribers
            EventBusProvider.getEventBus().post(new SettingsChangeEvent());

            return "home";
        }
    }
}
Guava EventBus allows to create any listener that is reacting for many different events - just annotate many methods with @Subscribe and that's all. Listeners can leverage existing events hierarchy. So if Listener A is waiting for events A, and event A has a subclass named B, this listener will receive both type of events: A and B. In our example, we posted three events: SettingsChangeEvent, LocaleChangeEvent and TimeZoneChangeEvent. The handleLocaleChange() method in the MyBean1 will only receive LocaleChangeEvent. The method handleTimeZoneChange() will only receive TimeZoneChangeEvent. But look at the method handleSettingsChange() in the MyBean2. It will receive all three events!

As you may see, a manually registration is still needed (EventBusProvider.getEventBus().register(this)) and the problem with scoped beans, I mentioned in the previous post, is still existing. We should be aware of scoping of EventBusProvider and scoping of publish / subscriber beans. But as you may also see, we have some improvements in comparison to the Mediator pattern: no special interfaces are needed, the subscriber's method names are not fix defined, multi-listeners are possible too, no effort to manage registered instances, etc. Last but not least - asynchronous AsyncEventBus and subscription to DeadEvent (for listening for any events which were dispatched without listeners - handy for debugging). Follow this guide please to convert an existing EventListener-based system to EventBus-based one.

CDI (Contexts and Dependency Injection)
Every JEE 6 compliant application server supports CDI (the JSR-299 specification). It defines a set of complementary services that help improve the structure of application code. The best-known implementations of CDI are OpenWebBeans and JBoss Weld. Events in CDI allow beans to interact with no dependency at all. Event producers raise events that are delivered to event observers by the container. This basic schema might sound like the familiar Observer / Observable pattern, but there are a couple of benefits.
  • Event producers and event observers are decoupled from each other.
  • Observers can specify a combination of "selectors" to narrow the set of event notifications they will receive.
  • Observers can be notified immediately or with delaying until the end of the current transaction.
  • No headache with scoping by conditional observer methods (remember problem of scoped beans and Mediator / EventBus?).
Conditional observer methods allow to obtain the bean instance that already exists, only if the scope of the bean that declares the observer method is currently active, without creating a new bean instance. If the observer method is not conditional, the corresponding bean will be always created. You are flexible!

CDI event mechanism is the best approach for the event-based communication in my opinion. The subject is complex. Let's only show the basic features. An observer method is a method of a bean with a parameter annotated @Observes.
public MyBean implements Serializable {

    public void onLocaleChangeEvent(@Observes Locale locale) {
        ...
    }
}
The event parameter may also specify qualifiers if the observer method is only interested in qualified events - these are events which have those qualifiers.
public void onLocaleChangeEvent(@Observes @Updated Locale locale) {
    ...
}
An event qualifier is just a normal qualifier, defined using @Qualifier. Here is an example:
@Qualifier
@Target({FIELD, PARAMETER})
@Retention(RUNTIME)
public @interface Updated {}
Event producers fire events using an instance of the parametrized Event interface. An instance of this interface is obtained by injection. A producer raises events by calling the fire() method of the Event interface, passing the event object.
public class UserSettingsForm implements Serializable {

    @Inject @Any Event<Locale> localeEvent;

    public void localeChangeListener(ValueChangeEvent e) {
        // notify all observers
        localeEvent.fire((Locale)e.getNewValue());
    }
}
The container calls all observer methods, passing the event object as the value of the event parameter. If any observer method throws an exception, the container stops calling observer methods, and the exception is re-thrown by the fire() method. @Any annotation above acts as an alias for any and all qualifiers. You see, no manually registration of observers is necessary. Easy? Specifying other qualifiers at the injection point is simple as well:
// this will raise events to observers having parameter @Observes @Updated Locale
@Inject @Updated Event<Locale> localeEvent;
You can also have multiple event qualifiers. The event is delivered to every observer method that has an event parameter to which the event object is assignable, and does not have any event qualifier except the event qualifiers matching those specified at the Event injection point. The observer method may have additional parameters, which are injection points. Example:
public void onLocaleChangeEvent(@Observes @Updated Locale locale, User user) {
    ...
}
What is about a specifying the qualifier dynamically? CDI allows to obtain a proper qualifier instance by means of AnnotationLiteral. This way, we can pass the qualifier to the select() method of Event. Example:
public class DocumentController implements Serializable {

    Document document;

    @Inject @Updated @Deleted Event<Document> documentEvent;

    public void updateDocument() {
        ...
        // notify observers with @Updated annotation
        documentEvent.select(new AnnotationLiteral<Updated>(){}).fire(document);
    }

    public void deleteDocument() {
        ...
        // notify observers with @Deleted annotation
        documentEvent.select(new AnnotationLiteral<Deleted>(){}).fire(document);
    }
}
Let's talk about "conditional observer methods". By default, if there is no instance of an observer in the current context, the container will instantiate the observer in order to deliver an event to it. This behaviour isn't always desirable. We may want to deliver events only to instances of the observer that already exist in the current context. A conditional observer is specified by adding receive = IF_EXISTS to the @Observes annotation.
public void onLocaleChangeEvent(@Observes(receive = IF_EXISTS) @Updated Locale locale) {
    ...
}
Read more about Scopes and Contexts here. In this short post we can not talk more about further features like "event qualifiers with members" and "transactional observers". I would like to encourage everybody to start learn CDI. Have much fun!

Edit: Found today a great CDI specific possibility how to obtain a list of listeners for an event. Solution: Inject BeanManager (allows to interact directly with the container) and call
<T> java.util.Set<ObserverMethod<? super T>> resolveObserverMethods(T event, java.lang.annotation.Annotation... qualifiers)

Friday, July 20, 2012

Tooltip on Steroids

PrimeFaces published today the blog post PickList on Steroids. I have decided to publish a similar post Tooltip on Steroids. PrimeFaces Extensions "injected steroids" into the Tooltip component. It's hefty strength now. This week I implemented three additional powerful features which I would like to share in this post.

Auto shown tooltips
The autoShow flag enables showing tooltips automatically after page loading. Auto shown tooltips are shown directly after the page has been loaded. Position of an auto shown tooltip is adjusted automatically when the window is resized. This feature is useful when you want to have a wizard or introduction tour with several infos showing when a site is entered the first time. Google and Facebook have a similar concept when introducing new features. Take a look please.


Using:
...
<p:inputText id="txt" title="Type something into this field"/>  
<pe:tooltip for="txt" autoShow="true" myPosition="left center" atPosition="right center"/>
...

Mouse tracking
Mouse tracking enables tooltip's positioning in relation to the mouse. The tooltip follows mouse when mouse moving. Take a look please.


Using:
...
<h:panelGroup id="demoMouse" layout="block" ...>
    Hover over this box to see mouse tracking in action  
</h:panelGroup>  
<pe:tooltip for="demoMouse" value="I position myself at the current mouse position"  
            mouseTracking="true" myPosition="top left" adjustX="10" adjustY="10"/>
...
As you might see, the tooltip also became new attributes adjustX and adjustY to control tooltip's coordinates related to the target element. These are positive or negative pixel values by which to offset the tooltip in the horizontal / vertical planes (x-axis / y-axis).

Client-side API
Tooltip became new widget's methods as well: hide(), show(), reposition() and destroy(). Hide() / show() allow to hide or show tooltips at runtime. Reposition() makes possible to adjust tooltip's position. You can use this method during scrolling, for instance. Destroy() removes tooltip's markup from the DOM. After that the tooltip is not available anymore.

Using:
...
<p:inputText id="txt" title="Type something into this field"/>  
<pe:tooltip for="txt" autoShow="true" widgetVar="tipWidget"/>

<p:commandButton type="button" value="Hide" onclick="tipWidget.hide()"/>
<p:commandButton type="button" value="Show" onclick="tipWidget.show()"/>
...

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.

Monday, April 23, 2012

DWR in JSF? Easy, with PrimeFaces Extensions.

Everybody knows DWR (Direct Web Remoting). DWR enables Java on the server and JavaScript in a browser to interact and call each other. It has several modules for Spring, Struts, Guice, etc. integration. There is also an JSF integration, but it's not fit enough to be used in JSF 2. It requires an expensive XML configuration or a configuration via annotations (@RemoteProxy, @RemoteMethod). The configuration describes a mapping between Java methods and JavaScript functions. JSF 2 standard lacks the ability to call arbitrary methods on JSF managed beans as well. There are some attempts to push up this intention and intergrate this technique in JSF 2, but nothing happend until now. PrimeFaces component library has a component RemoteCommand. Unfortunately, but it doesn't allow to call any Java method with any parameters. Passing parameters is not comfortable with this implementation. They have to be passed as
// call a JavaScript function bound to RemoteCommand with parameters
myFunction({param1: 'value1', param2: 'value2'});
and extracted from the current request on the server side. No JSF converters, validators can be applied, etc. Fortunately we have PrimeFaces Extensions project :-) which brings an own implementation of RemoteCommand. It covers two important use cases.

1) Assign JavaScript parameters to a property in a bean. It can be done in a convenient way with pe:remoteCommand and pe:assignableParam. AssignableParam specifies a name of the parameter and an EL expression for a corresponding Java method (setter) the parameter will be applied to. It allows to attach converters as well. The code below shows how it works.
<p:growl id="growl" showDetail="true" />
        
<pe:remoteCommand id="applyDataCommand" name="applyData" process="@this" update="subject date circle growl"
                  actionListener="#{remoteCommandController.parametersAssigned}">
    <pe:assignableParam name="subject" assignTo="#{remoteCommandController.subject}"/>
    <pe:assignableParam name="date" assignTo="#{remoteCommandController.date}">
        <f:convertDateTime type="both" dateStyle="short" locale="en"/>
    </pe:assignableParam>
    <pe:assignableParam name="circle" assignTo="#{remoteCommandController.circle}">
        <pe:convertJson />
    </pe:assignableParam>
</pe:remoteCommand>
   
<script type="text/javascript">
    circle = {
        radius: 50,
        backgroundColor: "#FF0000",
        borderColor: "#DDDDDD",
        scaleFactor: 1.2
    };
    circle2 = {
     ...
    };
</script>

<h:outputLabel for="subject" value="Subject: " />
<h:outputText id="subject" value="#{remoteCommandController.subject}" />

<h:outputLabel for="date" value="Date: " />
<h:outputText id="date" value="#{remoteCommandController.date}" />  

<h:outputLabel for="circle" value="Circle: " />
<h:outputText id="circle" value="#{remoteCommandController.circle.radius} - 
                 #{remoteCommandController.circle.backgroundColor} - 
                 #{remoteCommandController.circle.borderColor} - 
                 #{remoteCommandController.circle.scaleFactor}" />  

<p:commandButton value="Apply Data" type="button"
        onclick="applyData('hello world', '5/14/07 12:55:42 PM', JSON.stringify(circle))" />
<p:commandButton value="Apply Second Data" type="button"
        onclick="applyData('hello user', '7/11/01 11:55:42 PM', JSON.stringify(circle2))" />
You see that three parameters are passed to the JavaScript function applyData which is bound to the pe:remoteCommand via name attribute. Every pe:assignableParam takes a parameter and assign to the corresponding bean property via assignTo attribute. You can also see that an attached date converter converts date string to a date object (java.util.Date) and an JSON converter (from the PF Extensions project) converts a JavaScript object (circle) to a model Java class (Circle.java). The bean (controller class) and the Circle.java (model class) look simple:
@ManagedBean  
@RequestScoped  
public class RemoteCommandController {  
  
    private String subject;
    private Date date;
    private Circle circle;
 
    // getter, setter
  
    public void parametersAssigned() {  
        FacesMessage msg = new FacesMessage(FacesMessage.SEVERITY_INFO, "ActionListener called", "Parameters assigned");  
        FacesContext.getCurrentInstance().addMessage(null, msg);  
    }
}

public class Circle implements Serializable {  
  
    private int radius;
    private String backgroundColor;
    private String borderColor;
    private double scaleFactor;
  
    // getter, setter
}
Picture:

2) Call a remote Java method with any parameters from JavaScript. It can be done with pe:remoteCommand and pe:methodParam, pe:methodSignature. This is a DWR like approach with zero configuration. MethodParam specifies a name of the parameter and MethodSignature specifies a comma or space separated list with full qualified class names. Class names should match passed parameters in the same order as they were defined. The code below shows how it works.
<p:growl id="growl" showDetail="true" />

<pe:remoteCommand id="applyDataCommand" name="applyData" process="@this" update="growl"
                  actionListener="#{remoteCommandController.printMethodParams}">
    <pe:methodSignature parameters="java.lang.String, java.util.Date, org.primefaces.extensions.showcase.model.Circle" /> 
    <pe:methodParam name="subject"/>
    <pe:methodParam name="date">
        <f:convertDateTime type="both" dateStyle="short" locale="en"/>
    </pe:methodParam>
    <pe:methodParam name="circle">
        <pe:convertJson />
    </pe:methodParam>
</pe:remoteCommand>
   
<script type="text/javascript">
    circle = {
        radius: 50,
        backgroundColor: "#FF0000",
        borderColor: "#DDDDDD",
        scaleFactor: 1.2
    };
    circle2 = {
     ...
    };
</script>

<p:commandButton value="Apply Data" type="button"
        onclick="applyData('hello world', '5/14/07 12:55:42 PM', JSON.stringify(circle))" />
<p:commandButton value="Apply Second Data" type="button"
        onclick="applyData('hello user', '7/11/01 11:55:42 PM', JSON.stringify(circle2))" />
The bean method printMethodParams (defined as actionListener) will be called when user pushes one of the both command button. The method generates a message which is shown as growl popup.
@ManagedBean  
@RequestScoped  
public class RemoteCommandController {  
  
    public void printMethodParams(String subject, Date date, Circle circle) {  
        FacesMessage msg = new FacesMessage(FacesMessage.SEVERITY_INFO, "ActionListener called",  
                  "Subject: " + subject + ", Date: " + date + ", Circle - backgroundColor: " + circle.getBackgroundColor());  
  
        FacesContext.getCurrentInstance().addMessage(null, msg);  
    }
}
Converters are used as well. They can be also defined as usual in JSF by converter attribute in pe:assignableParam / pe:methodParam.

Described features are available in the PF Extensions 0.5-SNAPSHOT.

Edit: DWR allows a reverse call from Java to JavaScript as well. It works bidirectional. Well, it's possible with Atmosphere integration or PrimeFaces Push too. Furthermore, PrimeFaces provides a server side way to execute JavaScript when the ajax request completes. Example: RequestContext.getCurrentInstance().execute("dialog.hide()");