Sunday, 26 June 2011

Creating a Login Application with MVP framework and smartgwt widgets

GWT is an AJAX technology where the browser side code is written in java and is then compiled into javascript using GWT compiler.

Below are the steps to build a GWT application in eclipse.

1. Open Eclipse. Goto File-> New -> Web Application Project. Enter Projectname as Test and Package as com.mycompany.project as shown in Figure 1 and click Finish button.
Figure - 1

Wizard creates a sample project structure as shown in Figure - 2.


Figure - 2

Modify the package structure by adding following packages: activity, mvp, place and ui under client package.
Add LoginActivity.java and WelcomeActivity.java under activity package.
Add LoginPlace.java and WelcomePlace.java under place package.
Add  LoginView.java, LoginViewImpl.java, WelcomeView.java and WelcomeViewImpl.java under ui package.
Add AppActivityMapper.java and AppPlaceHistoryMapper.java under mvp package.
Add ClientFactory.java and ClientFactoryImpl.java under client package.
The resulting package structure under src should be as shown in figure-3


Figure -3

Code listing for the above mentioned classes is as below:


Test.gwt.xml
<?xml version="1.0" encoding="UTF-8"?>
<module rename-to='test'>
<!-- Inherit the core Web Toolkit stuff.                        -->
<inherits name='com.google.gwt.user.User'/>
<inherits name="com.google.gwt.place.Place"/>
<inherits name="com.google.gwt.activity.Activity"/>
<inherits name="com.smartgwt.SmartGwtNoTheme" />
<inherits name="com.smartclient.theme.enterpriseblue.EnterpriseBlue"/>
<inherits name="com.smartclient.theme.enterpriseblue.EnterpriseBlueResources"/>

<entry-point class="com.mycompany.project.client.Test"/>
<replace-with class="com.mycompany.project.client.ClientFactoryImpl">
<when-type-is class="com.mycompany.project.client.ClientFactory"/>
</replace-with>
<source path='client'/>
<source path='shared'/>

</module>

Test.java
package com.mycompany.project.client;

import com.google.gwt.activity.shared.ActivityManager;
import com.google.gwt.activity.shared.ActivityMapper;
import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.shared.EventBus;
import com.google.gwt.place.shared.Place;
import com.google.gwt.place.shared.PlaceController;
import com.google.gwt.place.shared.PlaceHistoryHandler;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.SimplePanel;
import com.mycompany.project.client.mvp.AppActivityMapper;
import com.mycompany.project.client.mvp.AppPlaceHistoryMapper;
import com.mycompany.project.client.place.LoginPlace;

public class Test implements EntryPoint {

private SimplePanel appWidget = new SimplePanel();
private Place defaultPlace = new LoginPlace("Hello");

public void onModuleLoad() {
// Create ClientFactory using deferred binding so we can replace with
// different impls in gwt.xml
ClientFactory clientFactory = GWT.create(ClientFactory.class);
EventBus eventBus = clientFactory.getEventBus();
PlaceController placeController = clientFactory.getPlaceController();
// Start ActivityManager for the main widget with our ActivityMapper
ActivityMapper activityMapper = new AppActivityMapper(clientFactory);
ActivityManager activityManager = new ActivityManager(activityMapper, eventBus);
activityManager.setDisplay(appWidget);
// Start PlaceHistoryHandler with our PlaceHistoryMapper
AppPlaceHistoryMapper historyMapper = GWT .create(AppPlaceHistoryMapper.class);
PlaceHistoryHandler historyHandler = new PlaceHistoryHandler(historyMapper);
historyHandler.register(placeController, eventBus, defaultPlace);
RootPanel.get().add(appWidget);
// Goes to place represented on URL or default place
historyHandler.handleCurrentHistory();
}
}

ClientFactory.java
package com.mycompany.project.client;

import com.google.gwt.event.shared.EventBus;
import com.google.gwt.place.shared.PlaceController;
import com.mycompany.project.client.ui.LoginView;
import com.mycompany.project.client.ui.WelcomeView;

/**
 * ClientFactory helpful to use a factory or dependency injection framework like GIN to obtain
 * references to objects needed throughout your application like the {@link EventBus},
 * {@link PlaceController} and views.
 */
public interface ClientFactory {

EventBus getEventBus();
PlaceController getPlaceController();

public LoginView getLoginView();
public WelcomeView getWelcomeView();
}

ClientFactoryImpl.java
package com.mycompany.project.client;

import com.google.gwt.event.shared.EventBus;
import com.google.gwt.event.shared.SimpleEventBus;
import com.google.gwt.place.shared.PlaceController;
import com.mycompany.project.client.ui.LoginView;
import com.mycompany.project.client.ui.LoginViewImpl;
import com.mycompany.project.client.ui.WelcomeView;
import com.mycompany.project.client.ui.WelcomeViewImpl;

/**
 * Sample implementation of {@link ClientFactory}.
 */
public class ClientFactoryImpl implements ClientFactory {

private static final EventBus eventBus = new SimpleEventBus();
private static final PlaceController placeController = new PlaceController(eventBus);
private static final LoginView loginView = new LoginViewImpl();
private static final WelcomeView welcomeView = new WelcomeViewImpl();

@Override
public EventBus getEventBus() {
return eventBus;
}

@Override
public PlaceController getPlaceController() {
return placeController;
}

@Override
public LoginView getLoginView() {
// TODO Auto-generated method stub
return loginView;
}

@Override
public WelcomeView getWelcomeView() {
// TODO Auto-generated method stub
return welcomeView;
}
}

LoginActivity.java
package com.mycompany.project.client.activity;

import java.util.List;

import com.google.gwt.activity.shared.AbstractActivity;
import com.google.gwt.event.shared.EventBus;
import com.google.gwt.place.shared.Place;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.AcceptsOneWidget;
import com.mycompany.project.client.ClientFactory;
import com.mycompany.project.client.place.LoginPlace;
import com.mycompany.project.client.place.WelcomePlace;
import com.mycompany.project.client.ui.LoginView;

public class LoginActivity extends AbstractActivity implements
LoginView.Presenter {

private ClientFactory clientFactory;

private LoginView loginView;
public List<String> contactDetails;

private String name;

public LoginActivity(LoginPlace place, ClientFactory clientFactory) {
this.name = place.getName();
this.clientFactory = clientFactory;
}

/**
* Invoked by the ActivityManager to start a new Activity
*/
@Override
public void start(AcceptsOneWidget containerWidget, EventBus eventBus) {
loginView = clientFactory.getLoginView();
loginView.setName(name);
loginView.setPresenter(this);
containerWidget.setWidget(loginView.asWidget());
}

/**
* Ask user before stopping this activity
*/
@Override
public String mayStop() {
return "Please hold on. This activity is stopping.";
}

/**
* Navigate to a new Place in the browser
*/
public void goTo(Place place) {

clientFactory.getPlaceController().goTo(place);
}

public void authenticate(final String username, String password) {
if (username.equalsIgnoreCase("admin") && password.equalsIgnoreCase("admin")) {
goTo(new WelcomePlace(username));
Window.alert("Login Success.");
}
else
Window.alert("Login not Success.");
}

}

WelcomeActivity.java

package com.mycompany.project.client.activity;

import com.google.gwt.activity.shared.AbstractActivity;
import com.google.gwt.event.shared.EventBus;
import com.google.gwt.place.shared.Place;
import com.google.gwt.user.client.ui.AcceptsOneWidget;
import com.mycompany.project.client.ClientFactory;
import com.mycompany.project.client.place.WelcomePlace;
import com.mycompany.project.client.ui.WelcomeView;

/**
 * Activities are started and stopped by an ActivityManager associated with a
 * container Widget.
 */
public class WelcomeActivity extends AbstractActivity implements
WelcomeView.Presenter {
/**
* Used to obtain views, eventBus, placeController. Alternatively, could be
* injected via GIN.
*/
private ClientFactory clientFactory;
private WelcomeView view;

/**
* Sample property.
*/
private String name;

public WelcomeActivity(WelcomePlace place, ClientFactory clientFactory) {
this.name = place.getName();
this.clientFactory = clientFactory;
}

@Override
public void start(AcceptsOneWidget containerWidget, EventBus eventBus) {
view = clientFactory.getWelcomeView();
view.setName(name);
view.setPresenter(this);
containerWidget.setWidget(view.asWidget());
}

@Override
public String mayStop() {
return "Please hold on. This activity is stopping.";
}

/**
* @see WelcomeView.Presenter#goTo(Place)
*/
public void goTo(Place place) {
clientFactory.getPlaceController().goTo(place);
}
}

LoginPlace.java
package com.mycompany.project.client.place;

import com.google.gwt.place.shared.Place;
import com.google.gwt.place.shared.PlaceTokenizer;

/**
 * A place object representing a particular state of the UI. A Place can be converted to and from a
 * URL history token by defining a {@link PlaceTokenizer} for each {@link Place}, and the
 * {@link PlaceHistoryHandler} automatically updates the browser URL corresponding to each
 * {@link Place} in your app.
 */
public class LoginPlace extends Place {

/**
* Sample property (stores token).
*/
private String name;

public LoginPlace(String token) {
this.name = token;
}

public String getName() {
return name;
}

/**
* PlaceTokenizer knows how to serialize the Place's state to a URL token.
*/
public static class Tokenizer implements PlaceTokenizer<LoginPlace> {

@Override
public String getToken(LoginPlace place) {
return place.getName();
}

@Override
public LoginPlace getPlace(String token) {
return new LoginPlace(token);
}

}
}

WelcomePlace.java
package com.mycompany.project.client.place;

import com.google.gwt.place.shared.Place;
import com.google.gwt.place.shared.PlaceTokenizer;
import com.google.gwt.place.shared.Prefix;

/**
 * A place object representing a particular state of the UI. A Place can be converted to and from a
 * URL history token by defining a {@link PlaceTokenizer} for each {@link Place}, and the
 * {@link PlaceHistoryHandler} automatically updates the browser URL corresponding to each
 * {@link Place} in your app.
 */
public class WelcomePlace extends Place {

/**
* Sample property (stores token).
*/
private String name;

public WelcomePlace(String token) {
this.name = token;
}

public String getName() {
return name;
}

/**
* PlaceTokenizer knows how to serialize the Place's state to a URL token.
*/
@Prefix("list")
public static class Tokenizer implements PlaceTokenizer<WelcomePlace> {

@Override
public String getToken(WelcomePlace place) {
return place.getName();
}

@Override
public WelcomePlace getPlace(String token) {
return new WelcomePlace(token);
}

}
}

AppActivityMapper.java
package com.mycompany.project.client.mvp;

import com.google.gwt.activity.shared.Activity;
import com.google.gwt.activity.shared.ActivityMapper;
import com.google.gwt.place.shared.Place;
import com.mycompany.project.client.ClientFactory;
import com.mycompany.project.client.activity.LoginActivity;
import com.mycompany.project.client.activity.WelcomeActivity;
import com.mycompany.project.client.place.LoginPlace;
import com.mycompany.project.client.place.WelcomePlace;

/**
 * ActivityMapper associates each {@link Place} with its corresponding {@link Activity}.
 */
public class AppActivityMapper implements ActivityMapper {

/**
* Provided for {@link Activitie}s.
*/
private ClientFactory clientFactory;

public AppActivityMapper(ClientFactory clientFactory) {
super();
this.clientFactory = clientFactory;
}

@Override
public Activity getActivity(Place place) {

if (place instanceof LoginPlace)
return new LoginActivity((LoginPlace) place, clientFactory);
else if (place instanceof WelcomePlace)
return new WelcomeActivity((WelcomePlace) place, clientFactory);
return null;
}

}

AppPlaceHistoryMapper.java

package com.mycompany.project.client.mvp;

import com.google.gwt.place.shared.PlaceHistoryMapper;
import com.google.gwt.place.shared.WithTokenizers;
import com.mycompany.project.client.place.WelcomePlace;
import com.mycompany.project.client.place.LoginPlace;

/**
 * PlaceHistoryMapper interface is used to attach all places which the PlaceHistoryHandler should
 * be aware of. This is done via the @WithTokenizers annotation or by extending
 * {@link PlaceHistoryMapperWithFactory} and creating a separate TokenizerFactory.
 */
@WithTokenizers({ LoginPlace.Tokenizer.class, WelcomePlace.Tokenizer.class })
public interface AppPlaceHistoryMapper extends PlaceHistoryMapper {
}

LoginView.java
package com.mycompany.project.client.ui;

import com.google.gwt.place.shared.Place;
import com.google.gwt.user.client.ui.IsWidget;

/**
 * View base interface.
 * Extends IsWidget so a view impl can easily provide its container widget.
 */
public interface LoginView extends IsWidget {

void setName(String helloName);

void setPresenter(Presenter listener);

public interface Presenter {
/**
* Navigate to a new Place in the browser.
*/
void goTo(Place place);
void authenticate(String username, String password);
}
}

LoginViewImpl.java
package com.mycompany.project.client.ui;

import com.google.gwt.user.client.ui.SimplePanel;
import com.google.gwt.user.client.ui.Widget;
import com.smartgwt.client.util.SC;
import com.smartgwt.client.widgets.form.DynamicForm;
import com.smartgwt.client.widgets.form.fields.ButtonItem;
import com.smartgwt.client.widgets.form.fields.TextItem;
import com.smartgwt.client.widgets.form.fields.events.ClickEvent;
import com.smartgwt.client.widgets.form.fields.events.ClickHandler;
import com.smartgwt.client.widgets.layout.VLayout;

/**
 * Sample implementation of {@link LoginView}.
 */
public class LoginViewImpl extends SimplePanel implements LoginView {
private Presenter listener;
private String name;

private VLayout mainLayout;

public LoginViewImpl() {

mainLayout = new VLayout();
mainLayout.setWidth100();
mainLayout.setHeight100();

DynamicForm form = new DynamicForm();
form.setWidth(300);
// form.setNumCols(2);

        final TextItem userName = new TextItem();
        userName.setName("userName");
        userName.setTitle("User Name");

        final TextItem password = new TextItem();
        password.setName("password");
        password.setTitle("Password");
     
        final ButtonItem buttonItem = new ButtonItem();
        buttonItem.setName("signin");
        buttonItem.setTitle("Sign In");
        buttonItem.setLeft(getAbsoluteLeft()+100);
        buttonItem.addClickHandler(new ClickHandler() {
            public void onClick(ClickEvent event) {
if (userName.getEnteredValue().length() == 0
|| password.getEnteredValue().length() == 0) {
SC.say("Username or password is empty.");
}
else {
listener.authenticate(userName.getEnteredValue(), password.getEnteredValue());
}            
            }
        });            
         
        form.setFields(userName, password, buttonItem);  

mainLayout.addMember(form);

}

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

@Override
public void setPresenter(Presenter listener) {
this.listener = listener;
}

public Widget asWidget() {
return mainLayout;
}

}

WelcomeView.java
package com.mycompany.project.client.ui;

import java.util.List;

import com.google.gwt.place.shared.Place;
import com.google.gwt.user.client.ui.IsWidget;
import com.smartgwt.client.widgets.grid.ListGridRecord;

/**
 * View base interface.
 * Extends IsWidget so a view impl can easily provide its container widget.
 */
public interface WelcomeView extends IsWidget {

void setName(String helloName);

void setPresenter(Presenter listener);

public interface Presenter {
/**
* Navigate to a new Place in the browser.
*/
void goTo(Place place);
}
}

WelcomeViewImpl.java
package com.mycompany.project.client.ui;

import java.util.List;

import com.google.gwt.user.client.ui.SimplePanel;
import com.google.gwt.user.client.ui.Widget;
import com.mycompany.project.client.ui.LoginView.Presenter;
import com.smartgwt.client.data.DataSource;
import com.smartgwt.client.data.Record;
import com.smartgwt.client.util.SC;
import com.smartgwt.client.widgets.IButton;
import com.smartgwt.client.widgets.Label;
import com.smartgwt.client.widgets.events.ClickHandler;
import com.smartgwt.client.widgets.events.CloseClickHandler;
import com.smartgwt.client.widgets.events.CloseClientEvent;
import com.smartgwt.client.widgets.form.DynamicForm;
import com.smartgwt.client.widgets.form.fields.ButtonItem;
import com.smartgwt.client.widgets.form.fields.TextItem;
import com.smartgwt.client.widgets.form.fields.events.ClickEvent;
import com.smartgwt.client.widgets.grid.ListGrid;
import com.smartgwt.client.widgets.grid.ListGridRecord;
import com.smartgwt.client.widgets.grid.events.RecordClickEvent;
import com.smartgwt.client.widgets.grid.events.RecordClickHandler;
import com.smartgwt.client.widgets.layout.HLayout;
import com.smartgwt.client.widgets.layout.VLayout;
import com.smartgwt.client.widgets.tab.Tab;
import com.smartgwt.client.widgets.tab.TabSet;
import com.smartgwt.client.widgets.tab.events.TabSelectedEvent;
import com.smartgwt.client.widgets.tab.events.TabSelectedHandler;
import com.smartgwt.client.widgets.viewer.DetailViewer;

/**
 * Sample implementation of {@link WelcomeView}.
 */
public class WelcomeViewImpl extends SimplePanel implements WelcomeView {
private Presenter listener;
private String name;

private VLayout mainLayout;

public WelcomeViewImpl() {

mainLayout = new VLayout();
mainLayout.setWidth100();
mainLayout.setHeight100();

Label label = new Label();
label.setContents("Welcome Home");

mainLayout.addMember(label);

}

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

@Override
public void setPresenter(Presenter listener) {
this.listener = listener;
}

public Widget asWidget() {
return mainLayout;
}
}



Right click on the project and select Run As -> Web Application.
The sample application comes up in the browser as shown in Figure-4.
Figure - 4