Skip to content

Home

Iván García Sainz-Aja edited this page Dec 26, 2013 · 54 revisions
Clone this wiki locally

GWT for Appfuse

This is a work in progress GWT implementation of Appfuse. UPDATE: Appfuse 3.0.0 GWT Archetypes.

the design of the application follows many of gwt latest best practices: MVP pattern, Activities and Places, EventBus, Gin and Guice..
http://code.google.com/p/gwt-best-practices-soup/

Right now the application implements most of Appfuse functionality providing some of the basic features any modern web application would require:

  • RPC/REST authentication mechanisms compatibles with standard form based login (it uses 'XMLHttpRequest' request header to choose proper responses)
  • Mode View Presenter pattern: with dumb views and Activities that see Views as simple interfaces that are accesed via setters and editors easily mockable
  • Proper history management: back button works as expected, views are bookmarkable, search criterias and pagination state details are also bookmarkables and work as expected through the back button
  • Login and session timeout are handled gracefully so you get 'redirected' to de view you have requested prior to the authentication event
  • Client or server side paging of search results
  • Configurable server side or client side local sorting
  • JSR-303 server and 'client' side validation through annotations or xml
  • Configurable abstract base classes to search/list/edit entities
  • Code spliting by Activity
  • Integration tests with Selenium 2

There will also be:

  • Mobile and Desktop front pages and navigation
  • Easy switch of views for different devices (activities could also be switched if required)

Getting started

You can try a test build compatible with Appfuse 3.0.0 of this GWT archetype running:

  • Basic GWT Archetype:

    mvn archetype:generate -B \
        -DarchetypeGroupId=org.appfuse.archetypes \
        -DarchetypeArtifactId=appfuse-basic-gwt-archetype \
        -DarchetypeVersion=3.0.0 \
        -DgroupId=com.mycompany \
        -DartifactId=myproject \
        -DarchetypeRepository=https://github.com/ivangsa/appfuse/raw/repo-3.0.0/
    
  • Modular GWT Archetype:

    mvn archetype:generate -B \
        -DarchetypeGroupId=org.appfuse.archetypes \
        -DarchetypeArtifactId=appfuse-modular-gwt-archetype \
        -DarchetypeVersion=3.0.0 \
        -DgroupId=com.mycompany \
        -DartifactId=myproject \
        -DarchetypeRepository=https://github.com/ivangsa/appfuse/raw/repo-3.0.0/
    

Once your project is created the quickest way to test it would be compiling GWT ''inplace'' and running it with jetty:

mvn clean gwt:clean compile gwt:compile -P gwtDebug -Dgwt.inplace=true jetty:run

This will compile gwt generated code to src/main/webapp/script and src/main/webapp/WEB-INF/deploy, so make sure you don't commit these files into source control. You can always remove them running mvn gwt:clean.

Using ''gwtDebug'' maven profile you can reduce the amount of translations that are compiled by GWT, see MainModuleDebug.gwt.xml.

NOTE: as per version 3.0.0 projects generated from these arechetypes would require some tweaks if appfuse:full-source plugins is run on the project:

  • Basic GWT Archetype: would require a the following line added to src/test/resources/mail.properties

    mail.port=25

  • Modular GWT Archetype: would require add/fix some of the missing dependency version properties on projects pom.xml...

Building from source

To get a grasp of what is going on right now you will need to download and build the source code.

Have a look at http://appfuse.org/display/APF/AppFuse+QuickStart about instructions to setup your environment. Once you are done setting up you enviroment:

  • Download source code from github

  • Currently you will need to build and install at least the service module:

cd service
mvn clean install
  • The quickets way to run the project would be compiling gwt inplace and runing jetty on the gwt module:
cd web/gwt
mvn clean gwt:clean compile gwt:compile -P gwtDebug -Dgwt.inplace=true jetty:run
  • Point your browser to http://localhost:8080/

Key Components and Resources

The application implements some of the described best practices for GWT development. It uses raw GWT Activities and Places bootstrapped with Gin, following the Model View Presenter pattern with the RequestFactory as transport mechanism, Editors Framework for databinding the model to ui widgets and JSR 303 validation in combination with RequestFactory and the Editors framework:

Other resources:

Client Application Structure

The main moving parts that glue together the rest of the application are as follows:

Application Structure

Component Stereotype Description
Application Base Class Instantiated by Gin, holds references to every other component
DesktopApplication Extends Application Bootstraps the client application for desktops
MobileApplication Extends Application Bootstraps the client application for mobile browsers
ApplicationMenu Class Defines menu entries tree, permissions and target places
ApplicationHistoryMapper Interface Marker interface for GWT Tokenizers
ApplicationActivityMapper Class Maps Places to its target Activity
ApplicationViewFactory Class Factory for instantiating and caching views, can be extended and configured via deferred binding to provide different view implementations to different clients
ApplicationRequestFactory Interface Factory interface to get remote RequestFactory services
ApplicationValidationFactory Interface Marker interface for GWT JSR-303 generated client side validators
ApplicationBeanFactory Class Configuration for serializing for Java Beans and ValueProxies from/to JSON. Its configuration is used to serialize search criteria to history tokens

Model View Presenter

Application follow Model-View-Presenter design pattern where Activities/Presenters see views a plain java interfaces with setters/getters and editors and view just delegates user interface events to its owner Activity/Controller through its Delegate interface.

Model View Presenter

EntityProxy Edit View and its Presenter Delegate interface

/**
 * Implemented by views that edit {@link EntityProxy}s.
 *
 * @param <P> the type of the proxy
 * @param <V> the type of this ProxyEditView, required to allow {@link #createEditorDriver()} to be correctly typed
 */
public interface ProxyEditView<P extends EntityProxy, V extends ProxyEditView<P, V>> extends IsWidget, HasEditorErrors<P> {

    /**
     * Implemented by the owner of the view.
     */
    interface Delegate {

        void cancelClicked();

        void deleteClicked();

        void saveClicked();
    }

    /**
     * Sets this view owner.
     * 
     * @param delegate
     */
    <D extends Delegate> void setDelegate(D delegate);

    /**
     * Creates a {@link RequestFactoryEditorDriver} for the editing {@link EntityProxy}.
     * 
     * @return a new {@link RequestFactoryEditorDriver} initialized to run this editor
     */
    RequestFactoryEditorDriver<P, ? extends V> createEditorDriver();

    /**
     * Enables/disables this view for editing.
     * 
     * @param b
     */
    void setEnabled(boolean b);
}

Search/List View and its Presenter Delegate interface

/**
 * Implemented by views that show a search form and its results list table for {@link EntityProxy}s.
 *
 * @param <P> the type of the records to display
 * @param <S> the type of this search criteria, can be a ValueProxy, a String or any other bean like type
 */
public interface ProxySearchView<P extends EntityProxy, S> extends IsWidget {

    /**
     * Implemented by the owner of the view.
     *
     * @param <P> the type of the records to display
     */
    interface Delegate<P> {

        void addClicked();
        void searchClicked();       
        void cancelClicked();

        void showDetails(String entityId);
        void deleteClicked(String entityId);
    }

    /**
     * Sets the delegate.
     */
    void setDelegate(Delegate delegate);

    /**
     * Sets the search criteria for this search form.
     * @param searchCriteria
     */
    void setSearchCriteria(S searchCriteria);

    /**
     * Gets the edited search criteria.
     * @return the edited search criteria
     */
    S getSearchCriteria();

    /**
     * Sets validation errors on the edited search criteria.
     * @param violations
     * @return true if there are any unconsumed validation errors. 
     */
    boolean setConstraintViolations(Iterable<ConstraintViolation<S>> violations);

    /**
     * Returns the records table as a {@link HasData}
     * @return
     */
    HasData<P> asHasData();

    /**
     * Gets column sorting information on the records table.
     * @return
     */
    ColumnSortList getColumnSortList();

    /**
     * Adds a column sort handler.
     * @param sortHandler
     * @see LocalColumnSortHandler for local client side sorting
     */
    void addColumnSortHandler(Handler sortHandler);

    /**
     * Returns the set of properties this view displays so they can be specifically requested to the server.
     * @return the set of properties this view displays
     */
    String[] getPaths();


    /**
     * Sets this records table page size.
     * @param pageSize
     */
    void setPageSize(Integer pageSize);
}

Activity - Place Controller

Application flow it's handled by the PlaceController and its configured ActivityMapper. Once an Activity it's started it may fire events to the EventBus or ask the PlaceController to navigate to another Place:

Activity - PlaceController

Something went wrong with that request. Please try again.