Archive for the ‘JavaServer Faces’ category

Automated Testing of RichFaces Components with Selenium

June 21st, 2011

It is pretty much accepted that Continuous Integration is an essential practice of any Agile software development. An essential part of Continuous Integration is automated testing.

When considering automated testing in a Continuous Integration environment one must consider the level of testing, the frequency and the degree of isolation.

Generally speaking, and from the perspective of a development team, I categorise testing as Unit testing, Integration testing and Acceptance testing. Acceptance testing takes the form of simulating the user interaction with the system and validating its behaviour.

In order to simulate the user interaction on a web interface, I often use Selenium. For the most part, I have found Selenium to provide the functionality I require from an automated testing framework. I use Maven to drive the acceptance tests and have found this works well.

The following maven pom will start a local instance of Tomcat, deploy the test subject (war), start an instance of the Selenium server, execute the Selenium tests, stop the Selenium server and stop the local instance of Tomcat.

» Read more: Automated Testing of RichFaces Components with Selenium

Web Form Validation with JBoss RichFaces and Hibernate Validator

February 9th, 2010

Last month I posted about Building a Dynamic Tree with JBoss RichFaces and Spring Web Flow. Since then, I have received a few requests to demonstrate how to implement validation within the modal dialog. There are several internet resources that already deal with this but none that provide a complete working example so I decided I would do just that.

You can download the source code in the form of a Maven project and follow along.

Hibernate Validator Dependencies

First step is to add the Hibernate Validator dependency to the project pom. I have used Hibernate Validator 4.0.0.GA because it is a JSR 303 compliant implementation.

<dependency>
  <groupId>org.hibernate</groupId>
  <artifactId>hibernate-validator</artifactId>
  <version>4.0.0.GA</version>
  <scope>compile</scope>
</dependency>

» Read more: Web Form Validation with JBoss RichFaces and Hibernate Validator

Building a Dynamic Tree with JBoss RichFaces and Spring Web Flow

January 19th, 2010

Recently I was involved in a project that required a user interface that would facilitate the manipulation of a hierarchical data structure.  I was already using JBoss RichFaces so it made perfect sense to utilise the tree and associated components (i.e. recursiveTreeNodesAdaptor and treeNode).  The project was also utilising the Spring Framework and Spring Web Flow and these were also incorporated into the eventual solution.

To start with, as any good developer does, I searched for existing solutions to the problem and while I found some useful information I did not find exactly what I was looking for.  With this in mind, when I eventually finished the development of the user interface I decided I would share my implementation and hopefully you will find what follows useful.

To allow you to follow along, I have made the source (maven project) available as a download.  This way, I do not have to worry about explaining everything in minute detail.

For a preview, you can always build the source, deploy to a server and navigate to http://[server]:[port]/dynamic-tree/spring/flow-main replacing the [server] and [port] with appropriate values.

The Data Structure

For the purpose of demonstrating my implementation, I am going to use a simple organisational hierarchy (as discussed in Martin Fowlers book ‘Analysis Patterns’) as illustrated below:

Figure 1: Data Structure

This is a very basic hierarchical data structure but it suffices for my purpose.  The implementation of this class is also very basic and requires little explanation.  To keep things simple, I have not considered the persistence mechanism that was employed.

package prototype.dynamictree.model;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

@Component
@Scope("prototype")
public class Organisation implements Serializable {

  private static final long serialVersionUID = -2023156931701914562L;

  private List<Organisation> children;

  private String description;

  private String name;

  private Organisation parent;

  public void addChild(Organisation child) {
    if (child.getParent() == null || !child.getParent().equals(this)) {
      child.setParent(this);
    }

    if (children == null) {
      children = new ArrayList<Organisation>();
    }

    if (!children.contains(child)) {
      children.add(child);
    }
  }

  public void removeChild(Organisation child) {
    if (child.getParent() != null && child.getParent().equals(this)) {
      child.setParent(null);
    }

    if (children != null) {
      children.remove(child);
    }
  }

  public List<Organisation> getChildren() {
    if (children == null) {
      children = new ArrayList<Organisation>();
    }

    return Collections.unmodifiableList(children);
  }

  public String getDescription() {
    return description;
  }

  public void setDescription(String description) {
    this.description = description;
  }

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public Organisation getParent() {
    return parent;
  }

  public void setParent(Organisation parent) {
    this.parent = parent;
  }
}

Strictly speaking, a Set would have been a more appropriate data structure for the Organisation’s children but I encountered strange behaviour by the tree component if Set was used as opposed to List.

Displaying the Hierarchical Data

As already mentioned, I used Spring Web Flow to manage the UI conversations and binding with the entity model.  The first step in the flow creates the root Organisation and sets a default name (Joe Bloggs Inc).  The second step is a view step that renders the hierarchical data using the tree component.

<rich:page
  xmlns:a4j="http://richfaces.org/a4j"
  xmlns:rich="http://richfaces.org/rich"
  xmlns:h="http://java.sun.com/jsf/html"
  xmlns:f="http://java.sun.com/jsf/core"
  markupType="xhtml" sidebarWidth="100" width="400"
  pageTitle="Dynamic Tree User Interface">

  <f:view>
    <h:form>
      <rich:panel id="dynamicTreePanel" header="Dynamic Tree User Interface">
        <rich:tree icon="/image/node.gif" iconLeaf="/image/node.gif">

          <rich:recursiveTreeNodesAdaptor roots="#{rootOrganisation}" var="_rootOrganisation">
            <rich:treeNode>
              <h:outputText value="#{_rootOrganisation.name}"/>
            </rich:treeNode>

            <rich:recursiveTreeNodesAdaptor roots="#{_rootOrganisation.children}"
                var="_childOrganisation" nodes="#{_childOrganisation.children}">

              <rich:treeNode>
                <h:outputText value="#{_childOrganisation.name}"/>
              </rich:treeNode>
            </rich:recursiveTreeNodesAdaptor>
          </rich:recursiveTreeNodesAdaptor>
        </rich:tree>
      </rich:panel>
    </h:form>
  </f:view>
</rich:page>

The reason I have used a nested recursiveTreeNodesAdaptor component is that later in the process, I will be associating a different contextMenu component with the root node than other nodes.

Upon executing the flow the following is rendered.

Figure 2: Rendered View

Creating the Node Selection Listener

In order to manipulate the tree structure, we will need to be able to determine the currently selected node.  This is achieved by creating a class to listen for the node selection event and configuring this class to receive the event.

The code for the class is straight forward and the listing is as follows:

package prototype.dynamictree.bean;

import java.io.Serializable;

import org.richfaces.component.html.HtmlTree;
import org.richfaces.event.NodeSelectedEvent;

public class DynamicTree implements Serializable {

  private static final long serialVersionUID = 4935795680279034480L;

  private Object selectedNodeData;

  public void processNodeSelection(NodeSelectedEvent event) {
    HtmlTree tree = ((HtmlTree) event.getComponent());

    selectedNodeData = tree.getRowData();
  }

  public Object getSelectedNodeData() {
    return selectedNodeData;
  }
}

The method getSelectedNodeData will return the Organisation associated with the selected node.

To configure the class to receive the node selection event (via ajax) the tree component attributes need to be amended as follows:

<rich:page
  xmlns:a4j="http://richfaces.org/a4j"
  xmlns:rich="http://richfaces.org/rich"
  xmlns:h="http://java.sun.com/jsf/html"
  xmlns:f="http://java.sun.com/jsf/core"
  markupType="xhtml" sidebarWidth="100" width="400"
  pageTitle="Dynamic Tree User Interface">

  <f:view>
    <h:form>
      <rich:panel id="dynamicTreePanel" header="Dynamic Tree User Interface">
        <rich:tree ajaxSubmitSelection="true"
            ajaxSingle="true"
            nodeSelectListener="#{dynamicTree.processNodeSelection}"
            rightClickSelection="true"
            icon="/image/node.gif" iconLeaf="/image/node.gif">

          <rich:recursiveTreeNodesAdaptor roots="#{rootOrganisation}" var="_rootOrganisation">
            <rich:treeNode>
              <h:outputText value="#{_rootOrganisation.name}"/>
            </rich:treeNode>

            <rich:recursiveTreeNodesAdaptor roots="#{_rootOrganisation.children}"
                var="_childOrganisation" nodes="#{_childOrganisation.children}">

              <rich:treeNode>
                <h:outputText value="#{_childOrganisation.name}"/>
              </rich:treeNode>
            </rich:recursiveTreeNodesAdaptor>
          </rich:recursiveTreeNodesAdaptor>
        </rich:tree>
      </rich:panel>
    </h:form>
  </f:view>
</rich:page>

Adding Support for Creating Child Nodes

Now that we are being notified of the node selection event we will be able to add child nodes to the tree component. This will achieved by adding 2 context menu components (one for the root node and one for all others) and a modal dialog.

<rich:page
  xmlns:a4j="http://richfaces.org/a4j"
  xmlns:rich="http://richfaces.org/rich"
  xmlns:h="http://java.sun.com/jsf/html"
  xmlns:f="http://java.sun.com/jsf/core"
  markupType="xhtml" sidebarWidth="100" width="400"
  pageTitle="Dynamic Tree User Interface">

  <a4j:loadStyle src="/css/style.css"/>

  <f:view>
    <h:form>
      <rich:panel id="dynamicTreePanel" header="Dynamic Tree User Interface">
        <rich:tree ajaxSubmitSelection="true"
            ajaxSingle="true"
            nodeSelectListener="#{dynamicTree.processNodeSelection}"
            rightClickSelection="true"
            icon="/image/node.gif" iconLeaf="/image/node.gif">

          <rich:recursiveTreeNodesAdaptor roots="#{rootOrganisation}" var="_rootOrganisation">
            <rich:treeNode>
              <h:outputText value="#{_rootOrganisation.name}"/>

              <rich:componentControl disableDefault="true" event="oncontextmenu"
                  for="rootContextMenu" operation="show"/>
            </rich:treeNode>

            <rich:recursiveTreeNodesAdaptor roots="#{_rootOrganisation.children}"
                var="_childOrganisation" nodes="#{_childOrganisation.children}">

              <rich:treeNode>
                <h:outputText value="#{_childOrganisation.name}"/>

                <rich:componentControl disableDefault="true" event="oncontextmenu"
                    for="childContextMenu" operation="show"/>
              </rich:treeNode>
            </rich:recursiveTreeNodesAdaptor>
          </rich:recursiveTreeNodesAdaptor>
        </rich:tree>
      </rich:panel>

      <!-- context menu's -->
      <rich:contextMenu id="rootContextMenu" attached="false" submitMode="ajax">
        <rich:menuItem>
          <h:outputText value="Add Child"/>

          <a4j:support event="onclick" action="addChild"
              reRender="dynamicTreePanel, addChildModalPanel"
              oncomplete="Richfaces.showModalPanel('addChildModalPanel')"/>
        </rich:menuItem>
      </rich:contextMenu>

      <rich:contextMenu id="childContextMenu" attached="false" submitMode="ajax">
        <rich:menuItem>
          <h:outputText value="Add Child"/>

          <a4j:support event="onclick" action="addChild"
              reRender="dynamicTreePanel, addChildModalPanel"
              oncomplete="Richfaces.showModalPanel('addChildModalPanel')"/>
        </rich:menuItem>
      </rich:contextMenu>

      <!-- modal panels -->
      <rich:modalPanel id="addChildModalPanel"
          resizeable="false" height="200" width="330">

        <f:facet name="header">
          <h:outputText value="Add Organisation"/>
        </f:facet>

        <h:panelGrid columns="2" rendered="#{addChildModalPanelRendered}"
            columnClasses="verticalAlignTop">

          <h:outputText value="Name:"/>
          <h:inputText value="#{addChildOrganisation.name}"/>

          <h:outputLabel value="Description:"/>
          <h:inputTextarea value="#{addChildOrganisation.description}"
              rows="5" cols="30"/>
        </h:panelGrid>

        <h:panelGroup style="display:block; text-align:center">
          <a4j:commandButton action="addChildModalPanelConfirm" value="Ok"
              onclick="Richfaces.hideModalPanel('addChildModalPanel')"
              reRender="dynamicTreePanel, addChildModalPanel"
              styleClass="commandButton"/>

          <a4j:commandButton action="addChildModalPanelCancel" value="Cancel"
              onclick="Richfaces.hideModalPanel('addChildModalPanel')"
              reRender="dynamicTreePanel, addChildModalPanel"
              styleClass="commandButton"/>
        </h:panelGroup>
      </rich:modalPanel>
    </h:form>
  </f:view>
</rich:page>

Figure 3: Context Menu

As you can see, when the user right clicks on a tree node, the appropriate context menu will be displayed. If the user then clicks on ‘Add Child’ the addChild flow transition is invoked (via ajax) which creates a child Organisation object. On return from this ajax call the dynamicTreePanel and addChildModalPanel will be re-rendered before displaying the modal panel.

Figure 4: Modal Panel

When the user clicks on the Ok button, the modal dialog is hidden and the addChildModalPanelConfirm flow transition is invoked. This transition adds the child Organisation to its parent and again on return from this ajax call the dynamicTreePanel and addChildModalPanel will be re-rendered.

When the user clicks on the Cancel button, the modal dialog is hidden and the addChildModalPanelCancel flow transition is invoked. On return from this ajax call the dynamicTreePanel and addChildModalPanel will be re-rendered.

Figure 5: Tree with Child Nodes

I’m sure the wide awake among you have also noticed the addChildModalPanelRendered property.  This property is used to make sure the components that refer to a child Organisation are not rendered if no child organisation exists in the current conversation (i.e. when we are not actually adding a child organisation).

Adding Support for Editing Node Data

Now that we are able to add a child node, it is relatively straight forward to add support for editing a node. To achieve this, we will have to add 2 menu items to the already existing context menus and add another modal dialog.

<rich:page
  xmlns:a4j="http://richfaces.org/a4j"
  xmlns:rich="http://richfaces.org/rich"
  xmlns:h="http://java.sun.com/jsf/html"
  xmlns:f="http://java.sun.com/jsf/core"
  markupType="xhtml" sidebarWidth="100" width="400"
  pageTitle="Dynamic Tree User Interface">

  <a4j:loadStyle src="/css/style.css"/>

  <f:view>
    <h:form>
      <rich:panel id="dynamicTreePanel" header="Dynamic Tree User Interface">
        <rich:tree ajaxSubmitSelection="true"
            ajaxSingle="true"
            nodeSelectListener="#{dynamicTree.processNodeSelection}"
            rightClickSelection="true"
            icon="/image/node.gif" iconLeaf="/image/node.gif">

          <rich:recursiveTreeNodesAdaptor roots="#{rootOrganisation}" var="_rootOrganisation">
            <rich:treeNode>
              <h:outputText value="#{_rootOrganisation.name}"/>

              <rich:componentControl disableDefault="true" event="oncontextmenu"
                  for="rootContextMenu" operation="show"/>
            </rich:treeNode>

            <rich:recursiveTreeNodesAdaptor roots="#{_rootOrganisation.children}"
                var="_childOrganisation" nodes="#{_childOrganisation.children}">

              <rich:treeNode>
                <h:outputText value="#{_childOrganisation.name}"/>

                <rich:componentControl disableDefault="true" event="oncontextmenu"
                    for="childContextMenu" operation="show"/>
              </rich:treeNode>
            </rich:recursiveTreeNodesAdaptor>
          </rich:recursiveTreeNodesAdaptor>
        </rich:tree>
      </rich:panel>

      <!-- context menu's -->
      <rich:contextMenu id="rootContextMenu" attached="false" submitMode="ajax">
        <rich:menuItem>
          <h:outputText value="Add Child"/>

          <a4j:support event="onclick" action="addChild"
              reRender="dynamicTreePanel, addChildModalPanel"
              oncomplete="Richfaces.showModalPanel('addChildModalPanel')"/>
        </rich:menuItem>

        <rich:menuSeparator/>

        <rich:menuItem>
          <h:outputText value="Edit"/>

          <a4j:support event="onclick" action="editChild"
              reRender="dynamicTreePanel, editChildModalPanel"
              oncomplete="Richfaces.showModalPanel('editChildModalPanel')"/>
        </rich:menuItem>
      </rich:contextMenu>

      <rich:contextMenu id="childContextMenu" attached="false" submitMode="ajax">
        <rich:menuItem>
          <h:outputText value="Add Child"/>

          <a4j:support event="onclick" action="addChild"
              reRender="dynamicTreePanel, addChildModalPanel"
              oncomplete="Richfaces.showModalPanel('addChildModalPanel')"/>
        </rich:menuItem>

        <rich:menuSeparator/>

        <rich:menuItem>
          <h:outputText value="Edit"/>

          <a4j:support event="onclick" action="editChild"
              reRender="dynamicTreePanel, editChildModalPanel"
              oncomplete="Richfaces.showModalPanel('editChildModalPanel')"/>
        </rich:menuItem>
      </rich:contextMenu>

      <!-- modal panels -->
      <rich:modalPanel id="addChildModalPanel"
          resizeable="false" height="200" width="330">

        <f:facet name="header">
          <h:outputText value="Add Organisation"/>
        </f:facet>

        <h:panelGrid columns="2" rendered="#{addChildModalPanelRendered}"
            columnClasses="verticalAlignTop">

          <h:outputText value="Name:"/>
          <h:inputText value="#{addChildOrganisation.name}"/>

          <h:outputLabel value="Description:"/>
          <h:inputTextarea value="#{addChildOrganisation.description}"
              rows="5" cols="30"/>
        </h:panelGrid>

        <h:panelGroup style="display:block; text-align:center">
          <a4j:commandButton action="addChildModalPanelConfirm" value="Ok"
              onclick="Richfaces.hideModalPanel('addChildModalPanel')"
              reRender="dynamicTreePanel, addChildModalPanel"
              styleClass="commandButton"/>

          <a4j:commandButton action="addChildModalPanelCancel" value="Cancel"
              onclick="Richfaces.hideModalPanel('addChildModalPanel')"
              reRender="dynamicTreePanel, addChildModalPanel"
              styleClass="commandButton"/>
        </h:panelGroup>
      </rich:modalPanel>

      <rich:modalPanel id="editChildModalPanel"
          resizeable="false" height="200" width="330">
        <f:facet name="header">
          <h:outputText value="Edit Organisation"/>
        </f:facet>

        <h:panelGrid columns="2" rendered="#{editChildModalPanelRendered}"
            columnClasses="verticalAlignTop">
          <h:outputText value="Name:"/>
          <h:inputText value="#{editChildOrganisation.name}"/>

          <h:outputText value="Description:"/>
          <h:inputTextarea value="#{editChildOrganisation.description}"
              rows="5" cols="30"/>
        </h:panelGrid>

        <h:panelGroup style="display:block; text-align:center">
          <a4j:commandButton action="editChildModalPanelConfirm" value="Ok"
              onclick="Richfaces.hideModalPanel('editChildModalPanel')"
              reRender="dynamicTreePanel, editChildModalPanel"
              styleClass="commandButton"/>

          <a4j:commandButton action="editChildModalPanelCancel" value="Cancel"
              onclick="Richfaces.hideModalPanel('editChildModalPanel')"
              reRender="dynamicTreePanel, editChildModalPanel"
              styleClass="commandButton"
              ajaxSingle="true"/>
        </h:panelGroup>
      </rich:modalPanel>
    </h:form>
  </f:view>
</rich:page>

As you can see, the context menu’s operate in the same way as before as does the flow transitions and modal dialogs. The main difference here is in the flow transition because rather that create a new child Organisation we are editing an existing one.

It is also worth pointing out the use of the ajaxSingle=”true” attribute on the Cancel commandButton component. The reason this is used is so that the model is not updated during the ajax request.

Adding Support for Removing Child Nodes

Similarly, it is relatively straight forward to add support for removing a child node. To achieve this, we will have to add 2 menu items to the already existing context menus and a transition to the flow step.

What’s Outstanding?

I did notice during this process is that there are some strange happenings that need to be investigated further.

  • When I use a Set as opposed to a List for the child variable in Organisation.java the tree does not bahave as expected.
  • When I try to remove the last child node from the root node the tree does not behave as expected

I realise there is some duplication in the above that should be removed but I do hope you have found this useful and look forward to your comments.

1 Star2 Stars3 Stars4 Stars5 Stars (2 votes, average: 5.00 out of 5)
Loading ... Loading ...

Practical RichFaces

January 8th, 2010

As I had mentioned in a previous post, I recently invested in a couple of books on JBoss RichFaces. Having read and reviewed JBoss RichFaces 3.3 I set about doing the same for the other book, Practical RichFaces.

The book started out giving the same background information on JSF, RichFaces and Ajax4Jsf but in contrast to JBoss RichFaces 3.3 stated that it will not be using JBoss Seam in the example’s which I was happy about. That is of course not a reflection on JBoss Seam but more my desire to read about RichFaces and not the many technologies that might complement it.

In the second chapter, the book suggests installing JBoss Tools and as I have not used this Eclipse plug-in before I decided I would do just that. It then proceeds to work through a small example to verify the Eclipse\JBoss Tools installation. This is done in some detail and would be quite useful if looking at RichFaces for the first time.

Some time is then spent introducing the main RichFaces components that support Ajax functionality, namely <a4j:comandLink>, <a4j:commandButton>, <a4j:support> and <a4j:poll>. These concepts were covered clearly and concisely with good use of appropriate examples. The remaining a4j components are covered in the chapter that follows.

Having covered the a4j components the book then proceeds to cover the majority of rich components dividing the coverage into input components, output components, data iteration components, selection components and menu components. The coverage of the input components is brief which is to be expected as it would be difficult to add anything to the JBoss RichFaces reference documentation. The coverage of the output components is a little more in-depth than the reference documentation and demonstrates, by example, some nice tips and tricks. The data iteration components, selection components and menu components are covered in roughly the same level of detail as that provided by the reference documentation.

The Scrollable Data Table and Tree are given a chapter of their own which covers the main functionality and usage of these components. As with JBoss RichFaces 3.3 I was disappointed with the level of coverage the Tree component received. This is one of the more complicated components and I would like to have seen more examples of its usage.

The last chapter on skinning is on a par with JBoss RichFaces 3.3 and covers the subject well.

My overall impression was again that I’m not really sure that this book adds anything to the freely available reference documentation but the tutorial style is certainly easier to read. I would recommend this book for someone starting out with RichFaces or for someone wanting to read a book end-to-end to get up to speed quickly. What is really needed for RichFaces is a RichFaces Recipe’s Book which is packed full of real world examples of using rich faces beyond the simple examples available on the RichFaces demo website.

One last comment that I would make on the book is that it got me wondering about the level of proof reading of these books. There are a number of glaring errors that would have been noticed if reviewed by anyone with RichFaces knowledge who reads this book. I think these types of mistakes should not make their way to print.

1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
Loading ... Loading ...

JBoss Rich Faces 3.3

December 16th, 2009

I have been developing with JBoss Rich Faces for a couple of years now and recently I decided to invest in a couple of books on the subject to try and obtain a better (more rounded) understanding of the framework. When purchasing books related to frameworks that already have good reference documentation I’m always sceptical that the book will provide additional insight above and beyond the freely available references.

One of the books I purchased was ‘JBoss Rich Faces 3.3′ which I set about reading from cover to cover. As with many of these books, it builds a sample application to demonstrate the use of the framework in a ‘real world’ application. In this instance, an advanced contact manager is developed.

The development of the application is demonstrated using Seam, Facelets, Hibernate JPA and Hibernate Validator which may or may not be desirable for the reader. I personally have no immediate interest in Seam and I found the level of content dedicated to Seam to be excessive. There are a number of books available on Seam and I would have purchased one of those had I wanted to read about Seam. I have used Hibernate JPA extensively and given that JPA is a standardised API it’s inclusion in the examples is welcome (it makes for more realistic examples).

The demonstration application used the AJAX components of Rich Faces extensively and this was one of the areas I wanted to focus on. Each of the AJAX components is discussed in the context of the contact manager application as well as smaller examples. This does a good job of relaying the key characteristics of the component and its attributes. In particular, the book does a reasonably good job of describing how the AJAX support components interact with the JSF lifecycle.

One thing that I would have liked to have seen was a significant working example of using the RichFaces tree components in conjunction with JPA entities. I have been toiling over this myself for some time now.

I’m not really sure that this book add’s anything to the freely available reference documentation but the tutorial style is certainly easier to read, particularly if you want to get up to speed quickly. Reading the reference documentation end-to-end would be as tedious as watching channel 4′s Big Brother dominated summer programming.

1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
Loading ... Loading ...