Here at Hive Development, I'm currently working on the GWT based UI for a new website/RIA monitoring service called SiteAlright. I recommend you head over and check it out. As you might expect we try to follow best practises when developing our apps and there's been quite a lot of talk recently on GWT Google Groups regarding the use of elements from the recent talk by Ray Ryan at Google I/O 2009 entitled Google Web Toolkit Architecture: Best Practices For Architecting Your GWTApp.
This excellent talk laid out several best practise approaches for architecting your GWT application based on the team's experiences while developing the new AdWords interface. Some of the key recommendations were:
- Use an MVP pattern;
- Use a command pattern;
- Use an Event Bus (a.k.a Event Collaboration);
- Use Dependency Injection.
The aim of this tutorial is to demonstrate these GWT best practises by applying them the default starter application generated by the Google Plugin for Eclipse.
There are many benefits to these patterns and I'll leave it to the links above to go into these in detail. However, using these patterns the bottom line is that your GWT application will be:
- Easier to extend - well defined structure makes the addition of new business logic much easier;
- Easier to test - the decoupling makes quick unit testing of commands, events and presenters not only possible but easy;
- GWT's History mechanism is baked into your application from the start - this is crucial for a GWT application and is a pain to retro-fit;
MVP Web Application Starter Project
For this tutorial I'm using Eclipse with the Google Plugin for Eclipse. With the Google plugin when you create a GWT project, it also creates a useful starter application with some widgets and server components. This tutorial will work with this starter application and will apply the above patterns.
Although this example has all the source code and references to required resources, you may download the complete GreetMvp project for Eclipse 3.5 from here.
As well as the Google plugin, you'll also need the following GWT libraries:
GWT-Presenter | An implementation of the MVP pattern; |
GWT-Dispatch | An implementation of the command pattern; |
Google Gin | Dependency Injection based on Google's Guice; |
GWT-Log | A log4j-style logger for GWT. |
NOTE: currently GIN needs to be built from source using SVN/Ant.
You'll also need the following libraries at the server:
log4j | A logging framework; |
Google Guice 2.0 | A dependency injection framework for Java. |
After introducing these best practise patterns, the structure starter application will be transformed. At this point, it's probably worth noting that:
- You'll no longer make RPC service calls directly - these are wrapped up in the command pattern and are handled by the gwt-dispatch library;
- The main page and the server response popup will be separated into respective view/presenters;
- The Event Bus pattern is implemented using the GWT 1.6+ Handlers mechanism.
Unsurprisingly, the code size will jump from the initial starter app, but what you'll end up with offers much more and will hopefully serve as the starting point for a real application based on these best practises.
Let's begin.
Fire up Eclipse and generate your starter application, I've called it GreetMvp:


This will generate the following structure:

If you're not already familiar with the default application I suggest you take a look at the entry point class which in my case is co.uk.hivedevelopment.greet.client.GreetMvp.java. You'll see all the view code, custom handler logic and server calls all lumped into one method. Fire up the application and you see the following:

The first thing we'll do is add the required libraries to the project - at this point you should have downloaded the listed dependencies and built Google Gin.
Create a folder called lib in your project at the top level of your project for client-only libraries and add the following:
gin.jar |
For server and client dependencies, add the remaining jars into the web application lib folder located at war/WEB-INF/lib:
aopalliance.jar | (from Google Gin) |
guice-2.0.jar | (from Google Gin. IMPORTANT - use the version supplied with Gin and not Guice) |
guice-servlet-2.0.jar | (from Google Guice) |
gwt-dispatch-1.0.0-SNAPSHOT.jar | (from gwt-dispatch) |
gwt-log-2.6.2.jar | (from gwt-log) |
gwt-presenter-1.0.0-SNAPSHOT.jar | (from gwt-presenter) |
log4j.jar | (from log4j) |
Add all of the above jars to the project's build path. You should have something that looks similar to this:

Edit the GWT module definition file co.uk.hivedevelopment.greet/GreetMvp.gwt.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE module PUBLIC "-//Google Inc.//DTD Google Web Toolkit 1.7.0//EN" "http://google-web-toolkit.googlecode.com/svn/tags/1.7.0/distro-source/core/src/gwt-module.dtd">
<module rename-to='greetmvp'>
<!-- Inherit the core Web Toolkit stuff. -->
<inherits name="com.google.gwt.user.User" />
<inherits name="com.google.gwt.inject.Inject" />
<inherits name='net.customware.gwt.dispatch.Dispatch' />
<inherits name='net.customware.gwt.presenter.Presenter' />
<!-- Inherit the default GWT style sheet. You can change -->
<!-- the theme of your GWT application by uncommenting -->
<!-- any one of the following lines. -->
<inherits name='com.google.gwt.user.theme.standard.Standard' />
<!-- <inherits name='com.google.gwt.user.theme.chrome.Chrome'/> -->
<!-- <inherits name='com.google.gwt.user.theme.dark.Dark'/> -->
<!-- Specify the app entry point class. -->
<entry-point class='co.uk.hivedevelopment.greet.client.GreetMvp' />
<!-- Add gwt-log support, default level `OFF` - check for
extended property 'log_level' to see if this is overridden -->
<inherits name="com.allen_sauer.gwt.log.gwt-log-OFF" />
<!-- Also compile Logger at `INFO` level -->
<extend-property name="log_level" values="INFO" />
<set-property name="log_level" value="INFO" />
<!-- Turn off the floating logger - output will be shown in the
hosted mode console -->
<set-property name="log_DivLogger" value="DISABLED" />
<source path="shared" />
<source path="client" />
</module>
Note the <source> tags. We have roughly 3 top level packages defined: client, shared and server. We do not want GWT accessing the server sub-packages so we have explicity told the GWT compiler what it can access.
Try to compile the project, it should compile cleanly.
Create View and Presenters
Now we'll split the app into the following components:
AppPresenter.java | Represents the main application; |
GreetingView.java | The GUI components for the greeting example; |
GreetingPresenter | The business logic for the greeting example; |
GreetingResponseView | The GUI for the reponse popup; |
GreetingResponsePresenter | The business logic for the response popup. |
GreetingView.java
package co.uk.hivedevelopment.greet.client.mvp;
import net.customware.gwt.dispatch.client.DispatchAsync;
import com.google.gwt.user.client.ui.HasWidgets;
import com.google.inject.Inject;
public class AppPresenter {
private HasWidgets container;
private GreetingPresenter greetingPresenter;
@Inject
public AppPresenter(final DispatchAsync dispatcher,
final GreetingPresenter greetingPresenter) {
this.greetingPresenter = greetingPresenter;
}
private void showMain() {
container.clear();
container.add(greetingPresenter.getDisplay().asWidget());
}
public void go(final HasWidgets container) {
this.container = container;
showMain();
}
}
GreetingPresenter.java
package co.uk.hivedevelopment.greet.client.mvp;
import net.customware.gwt.presenter.client.widget.WidgetDisplay;
import com.google.gwt.event.dom.client.HasClickHandlers;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.HasValue;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.Widget;
public class GreetingView extends Composite implements GreetingPresenter.Display {
private final TextBox name;
private final Button sendButton;
public GreetingView() {
final FlowPanel panel = new FlowPanel();
initWidget(panel);
name = new TextBox();
panel.add(name);
sendButton = new Button("Go");
panel.add(sendButton);
// Add the nameField and sendButton to the RootPanel
// Use RootPanel.get() to get the entire body element
RootPanel.get("nameFieldContainer").add(name);
RootPanel.get("sendButtonContainer").add(sendButton);
reset();
}
public HasValue getName() {
return name;
}
public HasClickHandlers getSend() {
return sendButton;
}
public void reset() {
// Focus the cursor on the name field when the app loads
name.setFocus(true);
name.selectAll();
}
/**
* Returns this widget as the {@link WidgetDisplay#asWidget()} value.
*/
public Widget asWidget() {
return this;
}
@Override
public void startProcessing() {
}
@Override
public void stopProcessing() {
}
}
GreetingResponseView.java
package co.uk.hivedevelopment.greet.client.mvp;
import net.customware.gwt.dispatch.client.DispatchAsync;
import net.customware.gwt.presenter.client.DisplayCallback;
import net.customware.gwt.presenter.client.EventBus;
import net.customware.gwt.presenter.client.place.Place;
import net.customware.gwt.presenter.client.place.PlaceRequest;
import net.customware.gwt.presenter.client.widget.WidgetDisplay;
import net.customware.gwt.presenter.client.widget.WidgetPresenter;
import co.uk.hivedevelopment.greet.shared.event.GreetingSentEvent;
import co.uk.hivedevelopment.greet.shared.rpc.SendGreeting;
import co.uk.hivedevelopment.greet.shared.rpc.SendGreetingResult;
import com.allen_sauer.gwt.log.client.Log;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.HasClickHandlers;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.HasValue;
import com.google.inject.Inject;
public class GreetingPresenter extends WidgetPresenter {
/**
* The message displayed to the user when the server cannot be reached or
* returns an error.
*/
private static final String SERVER_ERROR = "An error occurred while "
+ "attempting to contact the server. Please check your network "
+ "connection and try again.";
public interface Display extends WidgetDisplay {
public HasValue getName();
public HasClickHandlers getSend();
}
public static final Place PLACE = new Place("Greeting");
private final DispatchAsync dispatcher;
// FUDGE FACTOR! Although this is not used, having Gin pass the object
// to this class will force its instantiation and therefore will make the
// response presenter listen for events (via bind()). This is not a very good way to
// achieve this, but I wanted to put something together quickly - sorry!
private final GreetingResponsePresenter greetingResponsePresenter;
@Inject
public GreetingPresenter(final Display display,
final EventBus eventBus,
final DispatchAsync dispatcher,
final GreetingResponsePresenter greetingResponsePresenter) {
super(display, eventBus);
this.dispatcher = dispatcher;
this.greetingResponsePresenter = greetingResponsePresenter;
bind();
}
/**
* Try to send the greeting message
*/
private void doSend() {
Log.info("Calling doSend");
dispatcher.execute(new SendGreeting(display.getName().getValue()), new DisplayCallback(display) {
@Override
protected void handleFailure(final Throwable cause) {
Log.error("Handle Failure:", cause);
Window.alert(SERVER_ERROR);
}
@Override
protected void handleSuccess(final SendGreetingResult result) {
// take the result from the server and notify client interested components
eventBus.fireEvent(new GreetingSentEvent(result.getName(), result.getMessage()));
}
});
}
@Override
protected void onBind() {
// 'display' is a final global field containing the Display passed into the constructor.
display.getSend().addClickHandler(new ClickHandler() {
public void onClick(final ClickEvent event) {
doSend();
}
});
}
@Override
protected void onUnbind() {
// Add unbind functionality here for more complex presenters.
}
public void refreshDisplay() {
// This is called when the presenter should pull the latest data
// from the server, etc. In this case, there is nothing to do.
}
public void revealDisplay() {
// Nothing to do. This is more useful in UI which may be buried
// in a tab bar, tree, etc.
}
/**
* Returning a place will allow this presenter to automatically trigger when
* '#Greeting' is passed into the browser URL.
*/
@Override
public Place getPlace() {
return PLACE;
}
@Override
protected void onPlaceRequest(final PlaceRequest request) {
// Grab the 'name' from the request and put it into the 'name' field.
// This allows a tag of '#Greeting;name=Foo' to populate the name
// field.
final String name = request.getParameter("name", null);
if (name != null) {
display.getName().setValue(name);
}
}
}
GreetingResponsePresenter.java
package co.uk.hivedevelopment.greet.client.mvp;
import net.customware.gwt.presenter.client.widget.WidgetDisplay;
import com.google.gwt.event.dom.client.HasClickHandlers;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.DialogBox;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HasHTML;
import com.google.gwt.user.client.ui.HasText;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
public class GreetingResponseView extends DialogBox implements GreetingResponsePresenter.Display {
private final Label textToServerLabel;
private final HTML serverResponseLabel;
private final Button closeButton;
public GreetingResponseView() {
setText("Remote Procedure Call");
setAnimationEnabled(true);
closeButton = new Button("Close");
// We can set the id of a widget by accessing its Element
closeButton.getElement().setId("closeButton");
textToServerLabel = new Label();
serverResponseLabel = new HTML();
final VerticalPanel dialogVPanel = new VerticalPanel();
dialogVPanel.addStyleName("dialogVPanel");
dialogVPanel.add(new HTML("Sending name to the server:"));
dialogVPanel.add(textToServerLabel);
dialogVPanel.add(new HTML("Server replies:"));
dialogVPanel.add(serverResponseLabel);
dialogVPanel.setHorizontalAlignment(VerticalPanel.ALIGN_RIGHT);
dialogVPanel.add(closeButton);
setWidget(dialogVPanel);
}
public HasText getTextToServer() {
return textToServerLabel;
}
public HasHTML getServerResponse() {
return serverResponseLabel;
}
public HasClickHandlers getClose() {
return closeButton;
}
public DialogBox getDialogBox() {
return this;
}
/**
* Returns this widget as the {@link WidgetDisplay#asWidget()} value.
*/
public Widget asWidget() {
return this;
}
@Override
public void startProcessing() {
}
@Override
public void stopProcessing() {
}
}
package co.uk.hivedevelopment.greet.client.mvp;
import net.customware.gwt.presenter.client.EventBus;
import net.customware.gwt.presenter.client.place.Place;
import net.customware.gwt.presenter.client.place.PlaceRequest;
import net.customware.gwt.presenter.client.widget.WidgetDisplay;
import net.customware.gwt.presenter.client.widget.WidgetPresenter;
import co.uk.hivedevelopment.greet.shared.event.GreetingSentEvent;
import co.uk.hivedevelopment.greet.shared.event.GreetingSentEventHandler;
import com.allen_sauer.gwt.log.client.Log;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.HasClickHandlers;
import com.google.gwt.user.client.ui.DialogBox;
import com.google.gwt.user.client.ui.HasHTML;
import com.google.gwt.user.client.ui.HasText;
import com.google.inject.Inject;
public class GreetingResponsePresenter extends WidgetPresenter {
public interface Display extends WidgetDisplay {
HasText getTextToServer();
HasHTML getServerResponse();
HasClickHandlers getClose();
DialogBox getDialogBox();
}
public static final Place PLACE = new Place("GreetingResponse");
@Inject
public GreetingResponsePresenter(final Display display, final EventBus eventBus) {
super(display, eventBus);
bind();
}
@Override
protected void onBind() {
// Add a handler to close the DialogBox
display.getClose().addClickHandler(new ClickHandler() {
public void onClick(final ClickEvent event) {
display.getDialogBox().hide();
// Not sure of a nice place to put these!
// sendButton.setEnabled(true);
// sendButton.setFocus(true);
}
});
eventBus.addHandler(GreetingSentEvent.TYPE, new GreetingSentEventHandler() {
@Override
public void onGreetingSent(final GreetingSentEvent event) {
Log.info("Handling GreetingSent event");
display.getTextToServer().setText(event.getName());
display.getServerResponse().setHTML(event.getMessage());
display.getDialogBox().show();
}
});
}
@Override
protected void onUnbind() {
// Add unbind functionality here for more complex presenters.
}
public void refreshDisplay() {
// This is called when the presenter should pull the latest data
// from the server, etc. In this case, there is nothing to do.
}
public void revealDisplay() {
// Nothing to do. This is more useful in UI which may be buried
// in a tab bar, tree, etc.
}
/**
* Returning a place will allow this presenter to automatically trigger when
* '#GreetingResponse' is passed into the browser URL.
*/
@Override
public Place getPlace() {
return PLACE;
}
@Override
protected void onPlaceRequest(final PlaceRequest request) {
// this is a popup
}
}
At this point, we're still missing some code but hopefully you should start to see the structure coming together.
Events
Since this is a simple application, we only have one event - the GreetingSent event which has a corresponding handler:
GreetingSent.javaGreetingSentHandler.java
package co.uk.hivedevelopment.greet.shared.event;
import com.google.gwt.event.shared.GwtEvent;
public class GreetingSentEvent extends GwtEvent{
public static Type TYPE = new Type();
private final String name;
private final String message;
public GreetingSentEvent(final String name, final String message) {
this.name = name;
this.message = message;
}
public String getName() {
return name;
}
public String getMessage() {
return message;
}
@Override
public Type getAssociatedType() {
return TYPE;
}
@Override
protected void dispatch(final GreetingSentEventHandler handler) {
handler.onGreetingSent(this);
}
}
package co.uk.hivedevelopment.greet.shared.event;
import com.google.gwt.event.shared.EventHandler;
public interface GreetingSentEventHandler extends EventHandler {
void onGreetingSent(GreetingSentEvent event);
}
If you now look at the project references for the event and handler you can see where the events are fired and subsequently handled. The components are blissfully unaware of what produced the event and it just has the information that it needs to get the job done. Now imagine if you want to have another component that also reacts to this event, say to update another part of the GUI. Simple, just register another event handler - no spaghetti code.
RPC
Let's define client RPC. As mentioned earlier, we'll not be making the RPC calls directly. Instead we'll use the command pattern and let the gwt-dispatch library handle the underlying server calls. Although this is an implementation of the command pattern, it turns out that there is already a core GWT class called Command, so the authors of the gwt-dispatch library have opted to use Action instead - so "actions" are really "commands".
In our example, we'll define the Action SendGreeting which represent our server request and a SendGreetingResult class to encapsulate the server response:
SendGreeting.javaSendGreetingResult.java
package co.uk.hivedevelopment.greet.shared.rpc;
import net.customware.gwt.dispatch.shared.Action;
public class SendGreeting implements Action {
private static final long serialVersionUID = 5804421607858017477L;
private String name;
@SuppressWarnings("unused")
private SendGreeting() {
}
public SendGreeting(final String name) {
this.name = name;
}
public String getName() {
return name;
}
}
package co.uk.hivedevelopment.greet.shared.rpc;
import net.customware.gwt.dispatch.shared.Result;
public class SendGreetingResult implements Result {
private static final long serialVersionUID = 7917449246674223581L;
private String name;
private String message;
public SendGreetingResult(final String name, final String message) {
this.name = name;
this.message = message;
}
@SuppressWarnings("unused")
private SendGreetingResult() {
}
public String getName() {
return name;
}
public String getMessage() {
return message;
}
}
At this point, the client code should start to compile but there is still more to do before we can run it. We need some utility classes for the dispatcher and Gin:
CachingDispatchAsync.javaGreetingClientModule.java
package co.uk.hivedevelopment.greet.client;
import java.util.HashMap;
import java.util.Map;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.inject.Inject;
import net.customware.gwt.dispatch.client.DispatchAsync;
import net.customware.gwt.dispatch.shared.Action;
import net.customware.gwt.dispatch.shared.Result;
/**
* Dispatcher which support caching of data in memory
*
*/
public class CachingDispatchAsync implements DispatchAsync {
private DispatchAsync dispatcher;
private Map, Result> cache = new HashMap, Result>();
@Inject
public CachingDispatchAsync(final DispatchAsync dispatcher) {
this.dispatcher = dispatcher;
}
/*
* (non-Javadoc)
* @see net.customware.gwt.dispatch.client.DispatchAsync#execute(A, com.google.gwt.user.client.rpc.AsyncCallback)
*/
public , R extends Result> void execute(final A action, final AsyncCallback callback) {
dispatcher.execute(action, callback);
}
/**
* Execute the give Action. If the Action was executed before it will get fetched from the cache
*
* @param Action implementation
* @param Result implementation
* @param action the action
* @param callback the callback
*/
@SuppressWarnings("unchecked")
public , R extends Result> void executeWithCache(final A action, final AsyncCallback callback) {
final Result r = cache.get(action);
if (r != null) {
callback.onSuccess((R) r);
}
else {
dispatcher.execute(action, new AsyncCallback() {
public void onFailure(Throwable caught) {
callback.onFailure(caught);
}
public void onSuccess(R result) {
cache.put((Action) action, (Result) result);
callback.onSuccess(result);
}
});
}
}
/**
* Clear the cache
*/
public void clear() {
cache.clear();
}
}
GreetingGinjector.java
package co.uk.hivedevelopment.greet.client.gin;
import net.customware.gwt.presenter.client.DefaultEventBus;
import net.customware.gwt.presenter.client.EventBus;
import net.customware.gwt.presenter.client.gin.AbstractPresenterModule;
import net.customware.gwt.presenter.client.place.PlaceManager;
import co.uk.hivedevelopment.greet.client.CachingDispatchAsync;
import co.uk.hivedevelopment.greet.client.mvp.AppPresenter;
import co.uk.hivedevelopment.greet.client.mvp.GreetingPresenter;
import co.uk.hivedevelopment.greet.client.mvp.GreetingResponsePresenter;
import co.uk.hivedevelopment.greet.client.mvp.GreetingResponseView;
import co.uk.hivedevelopment.greet.client.mvp.GreetingView;
import com.google.inject.Singleton;
public class GreetingClientModule extends AbstractPresenterModule {
@Override
protected void configure() {
bind(EventBus.class).to(DefaultEventBus.class).in(Singleton.class);
bind(PlaceManager.class).in(Singleton.class);
bindPresenter(GreetingPresenter.class, GreetingPresenter.Display.class, GreetingView.class);
bindPresenter(GreetingResponsePresenter.class, GreetingResponsePresenter.Display.class, GreetingResponseView.class);
bind(AppPresenter.class).in(Singleton.class);
bind(CachingDispatchAsync.class);
}
}
package co.uk.hivedevelopment.greet.client.gin;
import net.customware.gwt.dispatch.client.gin.ClientDispatchModule;
import net.customware.gwt.presenter.client.place.PlaceManager;
import co.uk.hivedevelopment.greet.client.mvp.AppPresenter;
import com.google.gwt.inject.client.GinModules;
import com.google.gwt.inject.client.Ginjector;
@GinModules({ ClientDispatchModule.class, GreetingClientModule.class })
public interface GreetingGinjector extends Ginjector {
AppPresenter getAppPresenter();
PlaceManager getPlaceManager();
}
Now we tie all this together by replacing the existing module entry class with the following:
GreetMvp.java
package co.uk.hivedevelopment.greet.client;
import co.uk.hivedevelopment.greet.client.gin.GreetingGinjector;
import co.uk.hivedevelopment.greet.client.mvp.AppPresenter;
import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.ui.RootPanel;
public class GreetMvp implements EntryPoint {
private final GreetingGinjector injector = GWT.create(GreetingGinjector.class);
public void onModuleLoad() {
final AppPresenter appPresenter = injector.getAppPresenter();
appPresenter.go(RootPanel.get());
injector.getPlaceManager().fireCurrentPlace();
}
}
Server Components
Okay, now let's define the server side. We need to configure Guice and the dispatch handler plus we need to provide an implementation for the SendGreeting action.
SendGreetingHandler.javaDispatchServletModule.java
package co.uk.hivedevelopment.greet.server.handler;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import net.customware.gwt.dispatch.server.ActionHandler;
import net.customware.gwt.dispatch.server.ExecutionContext;
import net.customware.gwt.dispatch.shared.ActionException;
import org.apache.commons.logging.Log;
import co.uk.hivedevelopment.greet.shared.rpc.SendGreeting;
import co.uk.hivedevelopment.greet.shared.rpc.SendGreetingResult;
import com.google.inject.Inject;
import com.google.inject.Provider;
public class SendGreetingHandler implements ActionHandler<SendGreeting, SendGreetingResult> {
private final Log logger;
private final Provider<ServletContext> servletContext;
private final Provider<HttpServletRequest> servletRequest;
@Inject
public SendGreetingHandler(final Log logger,
final Provider<ServletContext> servletContext,
final Provider<HttpServletRequest> servletRequest) {
this.logger = logger;
this.servletContext = servletContext;
this.servletRequest = servletRequest;
}
@Override
public SendGreetingResult execute(final SendGreeting action,
final ExecutionContext context) throws ActionException {
final String name = action.getName();
try {
String serverInfo = servletContext.get().getServerInfo();
String userAgent = servletRequest.get().getHeader("User-Agent");
final String message = "Hello, " + name + "!<br><br>I am running " + serverInfo
+ ".<br><br>It looks like you are using:<br>" + userAgent;
//final String message = "Hello " + action.getName();
return new SendGreetingResult(name, message);
}
catch (Exception cause) {
logger.error("Unable to send message", cause);
throw new ActionException(cause);
}
}
@Override
public void rollback(final SendGreeting action,
final SendGreetingResult result,
final ExecutionContext context) throws ActionException {
// Nothing to do here
}
@Override
public Class<SendGreeting> getActionType() {
return SendGreeting.class;
}
}
LogProvider.java
package co.uk.hivedevelopment.greet.server.guice;
import net.customware.gwt.dispatch.server.service.DispatchServiceServlet;
import com.google.inject.servlet.ServletModule;
public class DispatchServletModule extends ServletModule {
@Override
public void configureServlets() {
// NOTE: the servlet context will probably need changing
serve("/greetmvp/dispatch").with(DispatchServiceServlet.class);
}
}
MyGuiceServletConfig.java
package co.uk.hivedevelopment.greet.server.guice;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.impl.Log4JLogger;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@Singleton
public class LogProvider implements Provider<Log>{
public Log get() {
return new Log4JLogger("GreetingLogger");
}
}
ServerModule.java
package co.uk.hivedevelopment.greet.server.guice;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.servlet.GuiceServletContextListener;
public class MyGuiceServletConfig extends GuiceServletContextListener {
@Override
protected Injector getInjector() {
return Guice.createInjector(new ServerModule(), new DispatchServletModule());
}
}
package co.uk.hivedevelopment.greet.server.guice;
import net.customware.gwt.dispatch.server.guice.ActionHandlerModule;
import org.apache.commons.logging.Log;
import co.uk.hivedevelopment.greet.server.handler.SendGreetingHandler;
import com.google.inject.Singleton;
/**
* Module which binds the handlers and configurations
*
*/
public class ServerModule extends ActionHandlerModule {
@Override
protected void configureHandlers() {
bindHandler(SendGreetingHandler.class);
bind(Log.class).toProvider(LogProvider.class).in(Singleton.class);
}
}
Finally we need to reconfigure the web.xml with the following:
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<!-- Remote logging agent for gwt-log -->
<servlet>
<servlet-name>remoteLoggerServiceImpl</servlet-name>
<servlet-class>com.allen_sauer.gwt.log.server.RemoteLoggerServiceImpl</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>remoteLoggerServiceImpl</servlet-name>
<url-pattern>/greetmvp/gwt-log</url-pattern>
</servlet-mapping>
<filter>
<filter-name>guiceFilter</filter-name>
<filter-class>com.google.inject.servlet.GuiceFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>guiceFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<listener>
<listener-class>co.uk.hivedevelopment.greet.server.guice.MyGuiceServletConfig</listener-class>
</listener>
<!-- Default page to serve -->
<welcome-file-list>
<welcome-file>GreetMvp.html</welcome-file>
</welcome-file-list>
</web-app>
You may now launch the application. Surprise surprise, it looks no different but you're now following best practises. Good luck with your own GWT MVP development.

Thank you very much for this write up!
ReplyDeleteI was trying to get my head around a lot of these things and this sure does help.
Cheers,
Yesudeep.
WhollyTricks
DeleteAirtel net Balance Check
Idea net Balance Check
Aircel net Balance Check
Vodafone net Balance Check
Reliance net Balance Check
Reliance Jio net Balance Check
BSNL net Balance Check
Docomo net Balance Check
DeleteTelenor net Balance Check
MTNL net Balance Check
Videocon net Balance Check
MTS net balance Check
Ussd Codes
Aircel Number Check
Airtel Loan Number
DeleteAirtel Loan Number
Airtel Loan Number
Airtel Loan Number
Idea Loan Number
Idea Loan Number
Idea Loan Number
Idea Loan Numberk
Birthday Wishes for Niece
Birthday Wishes for Daughter
How does the back/forward button work with this?
ReplyDeleteWhen you click the "Go" button, does the url update to be GreetMvp.html#name=Chris? It doesn't appear to have done that in your example.
That's the longest Hello World I've ever seen!
ReplyDeleteThis comment has been removed by the author.
ReplyDeleteWe are just implementing all of this. Fantastic right up !!
ReplyDeleteIt'll help a lot.
Thanks. Two remarks:
ReplyDelete1. The code in the blog is not syntactically correct in some places (missing "<"s at least).
2. There are no unit tests. How can it be a best practice if there is no tests ;) ?
Cheers,
S.
Google webtoolkit is awesome! Satta Matka
DeleteThank you all for your feed back.
ReplyDelete@brendan - You're right, I'm missing a hook. I'll provide a post soon (in day or so) covering this;
@Ricardo - lol, sorry it was a bit of a long one! Thankfully, adding functionality is much quicker.
@Stefane - I did have a bit of a wrestle with Blogger and the SyntaxHighlighter library to get things displaying. You can download the project complete from a link near the top of the post. Agreed about the unit tests. I want to add those in another post as this post was long enough already - I'll try to get something done this week.
There is a unstated dependency on the event bus library. Where is it available?
ReplyDeletewww.Guidingweb.info
Deletesunny leone hot images
kareena kapoor bikini
download hot images
sunny loene bikini
sexy photo
priyanka chopra bikini
Sunny leone Bikini
@arouse EventBus is defined as part of the gwt-presenter library.
ReplyDeleteI think everyone should remember that every pattern is appropriate to a particular use case or problem that you are trying to solve. Hello world unfortunately possesses none of these characteristics.
ReplyDeleteFor example, if you have multiple event sources and multiple event targets for one event, an event bus (or any other Observer pattern implementation) might be in order. Not the case here.
Likewise MVP has a place where one might switch model, view or presenter. Otherwise, keep the view and model and eliminate the presenter.
I really think what may be appropriate for Google Wave is a bit of overkill for the run of the mill CRUD application screen.
I keep getting this error: "[ERROR] Type 'net.customware.gwt.dispatch.shared.Action<?>' was not serializable and has no concrete serializable subtypes"...Any idea what might be wrong here? I'm using gwt-dispatch from its Maven repository on Google code.
ReplyDelete@tony - thanks for the comment. You're right, MVP is overkill for an app of Hello World size and I agree that using patterns for patterns sake is often a bad idea.
ReplyDeleteThis post was just meant to demonstrate the necessary components for MVP in the smallest form possible. When I was trying to get started with MVP I found myself spending a lot of time in Google trying to stitch these principles together, hence I thought some people may find this post useful. The actual app that I'm working on is must larger in scope and MVP is very appropriate.
If you're looking for a larger example then take a look at Hupa "GWT based Webmail for IMAP-Servers" in Apache Labs:
http://svn.apache.org/repos/asf/labs/hupa/
To be honest though, I'm finding that separating code into view and presenter, using DI and commands are working out pretty well and I would actually recommend it for anything but the simplest of apps. Obviously that's my taste. I used to have much of the GWT app code lumped together as composite classes and that got messy very quickly - the spaghetti situation mentioned in the Google I/O talk rang very true to me. Life is definitely much sweeter now.
@Kevin - I'm not sure what's going on there. Have you tried downloading the complete project from the link towards the start of this post?
ReplyDeletehttp://static1.hivedevelopment.co.uk/blog/gwt-best-practise-mvp-di-command/GreetMvp.zip
Chris: the reported error is with gwt-dispatch, not your code. I think it may have something to do with my compiler settings, though I'm not sure. I'll play around with it more.
ReplyDeleteAlso, I have a question regarding efficiency. The injector initializes everything at startup (through binding, am I right?) and the views are initialized with the presenter, but not all views are going to be used in a larger application. How does MVP tackle this problem? (I know it's not a problem for HelloWorld, but when I look at Hupa, it initializes all the widgets in the view's constructor)
Maybe I'm missing something here?
Thanks for the writeup.
@Kevin - if you're struggling with gwt-dispatch there is a version supplied with the project link which was built from source last Thursday.
ReplyDeleteThe injector does indeed initialise everything so you're quite right - when projects get large this can become a problem with unused presenters gobbling resource. Here you could consider moving widget creation code from the c'tor into a method can be called lazily. In the presenter instead of accessing 'display' directly, you could override BasicPresenter.getDisplay and add a call to Display.startProcessing() before returning the display reference. I couldn't see where the start/stopProcessing hooks are called by the presenter library (and their intent is not clear), so I suppose you could use them to manage your lazy initialisation code? Or extend WidgetDisplay to define your own.
Also, the presenter defines revealDisplay() but that is only called if using the Place logic which may not be appropriate if a page is composed of several presenters (only one can be called). For example, call the example using the following URL and refresh:
http://localhost:8080/GreetMvp.html#Greeting;name=Foo
As we've specified #Greeting, this will cause the GreetingPresenter's revealDisplay to be called.
Good luck.
All that is left now is to integrate the "Gateway"-Pattern mentioned in another talk at I/0 2009 that eases the building of "GWT.runAsync"-Modules.
ReplyDelete@Kevin: This error is commonly output by GWT when one of the following is true:
ReplyDelete1. Your Action/Result classes are not serializable. The most common causes are not having an empty constructor and having non-serializable fields in the class.
2. You are not actually using any Action/Result instances anywhere in your code, so GWT doesn't know about any valid subclasses.
Not sure if either of these might be the case for you.
Thanks for this nice example! I have a question i hope not too stupid (i'm new to all this, and not very good in english) :
ReplyDeleteIt seems that the "google getting started" way to implement a GWT-RPC is not used anymore. So we can forget about client-side Service/ServiceAsync interfaces + server-side ServiceImpl (always present in you example) and benefit from the command pattern. But what if i want to use Spring Security + Hibernate and Gilead for client-side persistent ejb? I have trouble to implement this in the server-side Handler of my services : Before that, i had a ServiceImpl with Gilead bean manager+Persistent hibernate, extending a DI (Spring) class for Security and Spring, extending a Persistence Class (Gilead), finally extending the RemoteServiceServlet.
i just followed/blend Wah Cheng blog : http://seewah.blogspot.com/2009/02/gwt-and-spring-security.html and Gilead ... but now i can't get it to work with this new command pattern way. Is there any other way to secure and persist ejb with gilead with command pattern?
@David - cheers for the input mate, appreciated;
ReplyDelete@Alcor - I hope to add a few more articles soon on unit tests and maybe try to add something on history management. However one thing I've had to do very recently on my current project is integrate the command pattern with JBoss Seam at the server where previously I was using plain old RPC. You'll be pleased to hear that the conversion was fairly straight forward and I'm sure you could use many of the steps I used as base for integrating with Spring? I'll try to add a quick article this evening while it's fresh in my mind.
The result passed back from a command execution can be anything you like as long as it's serialisable and implements the Result marker interface. This includes Hibernate objects. But BE WARNED that serialising Hibernate objects directly is a bad idea where cross graph dependencies, proxied collections and Timestamps are amongst some of the things waiting to bite you. Here you have a couple of options: use DTOs or process the Hibernate objects in some way before sending them over the wire. There's a book called "Pro Web 2.0 Application Development with GWT" by Jeff Dwyer that presents a solution to the latter problem by way of a Hibernate filter through which you can prepare a Hibernate object for serialisation. Full source of the book's application is online:
http://github.com/jdwyah/tocollege.net/blob/f97ed4ff3f31c1c463eb426c34a55de439c601b3/src/main/java/com/apress/progwt/server/gwt/HibernateFilter.java
The book also uses Spring MVC + Acegi security for its data services so it's probably worth having a good poke around that project.
I hope that's useful.
C.
Thanks for your help and advices. I'll read carefully the git and at your forthcoming article.
ReplyDeleteNB : I just saw the GWT-dispatch code with now Std/SecureDispatchServletModule. Is it to integrate or emulate a security scheme?
How is it best to get rid of the "FUDGE FACTOR" ?
ReplyDelete@Craig - good question, it was only a matter of time before I was pulled up on that ;)
ReplyDeleteIn the GreetMvp example the fudge factor was to inject the GreetingResponsePresenter in order for Gin to create an instance which is bound to the GreetingSentEvent. I think the approach should have been to let the GreetingPresenter take care of these events and control the showing of the popup.
I've been experimenting with a solution for pop-ups which probably needs a post of its own. Basically I created a PopupDelegateView/Presenter which wraps a regular View/Presenter to take care of common pop-up related stuff - so you can show a view as a pop-up or inline. I'm holding off posting something until I've given it more of a test, but it shouldn't be too long (a week or so).
Of course, if anyone else has some suggestions on pop-ups then I'd love to hear about them.
I hope that helps a bit?
Hi Chris thanks for the reply
ReplyDeleteWhat am I am trying to achieve is the following....
I have a login event and handerer, on return from I wish to raise 2 events (maybe more) that other parts of the system listen to ie a message handler etc. I cant see how I can make multiple presenters listen to the same event?
Is this possible?
Thanks
Craig
Morning Craig,
ReplyDeleteThat's certainly possible, in fact it's one of the main benefits of using the event based approach. Presumably you have some form of login action? All you should have to do is issue the necessary events on login success:
private void doSend() {
dispatcher.execute(new SendGreeting(display.getName().getValue()), new DisplayCallback(display) {
@Override
protected void handleFailure(final Throwable cause) {
...
}
@Override
protected void handleSuccess(final LoginResult result) {
eventBus.fireEvent(new MyPostLoginEvent(result.getSomeResult()));
... // more events?
}
});
}
Then simply add a handler in each presenter where you want to observe these events:
eventBus.addHandler(MyPostLoginEvent.TYPE, new MyPostLoginEventHandler() {
@Override
public void onGreetingSent(final MyPostLoginEvent event) {
Log.info("Do post login processing here");
}
});
An off the top of my head troubleshooting list for why some of the events might not fire:
Has each presenter been setup in your Gin client module?
Does the constructor for each presenter call bind()?
Does bind() subscribe to the specfied event?
Good luck.
Slight edit to last response:
ReplyDeleteI forgot to change doSend and the SendGreeting to better illustrate the point. They should have been
private void doLogin() {
dispatcher.execute(new LoginAction(display.getName().getValue(), display.getPassword().getValue())), new DisplayCallback(display) {
...
}
@Alcor - I've not looked into those additional classes since I'm bypassing the gwt-dispatch servlet modules altogether. I intend to use regular Seam security for my application.
ReplyDeleteI've now posted the instructions for the Seam integration here:
http://blog.hivedevelopment.co.uk/2009/08/integerating-gwt-dispatch-with-jboss.html
Hopefully that will give you enough of a starting point to adapt a Spring version?
Good luck,
C.
Very, very helpful. Thank you for posting this! Keep 'em coming!
ReplyDeleteAre we sure CachingDispatchAsync is actually being used here in lieu of the default dispatcher?
ReplyDeleteI tried making some changes to it, but they didn't show up in execution. I then went back to basics and just added some logging statements to the CachingDispatchAsync's execute method, but it doesn't get called either.
I don't fully understand how Gin injection works so I can't really fully see what's going on or whether the CachingAsyncDispatch class is getting used or not. Can anyone advise? Would be very helpful.
Thank you very much for this post. Regarding the fudge factor, would it be feasible to inject all the presenters in the appPresenter in an effort to keep the fudge centralized, or do you see any problems in that approach?
ReplyDeleteWhat part of the application would you make responsible for hiding old views? The presenter that fires an event that triggers the display of a new view, or the presenter that will display the new view? Or perhaps the presenter of the view that contains the two views?
Thanks for posting this nice tutorial! I have been messing with it today to use Spring on the server side. I've documented what I did here.
ReplyDeleteThis article should be rated PG-21 . its meant for mature GWT developers who have at one point encountered the issued being addressed here. I am one of them and it has really helped. Kind regards.
ReplyDeleteJoshua.
@aaa (if that is your real name ;)
ReplyDeleteGuilty as charged. Unfortunately CachingDispatchAsynch isn't a plug in replacement for the default DispatchAsynch implementation so you'll need to refer to it explicitly in your presenters. To use CachingDispatchAsynch, update the necessary presenters as follows:
1. Update the c'tor parameters to use the CachingDispatchAsynch along with the associated class property type;
2. Change calls to dispatcher.execute() to dispatcher.executeWithCache().
Cheers,
C.
@sorenbs - it's probably not advisable to inject all presenters into appPresenter. I have come up with a better solution to the original popup issue, so in the case of the example it will eliminate the need for the fudge factor. I'll try and post something very soon.
ReplyDeleteI've yet to properly implement lazy loading (and therefore associated tear down) code of my views. I believe that the whole issue of lazy loading is being looked at for the next version of gwt-presenter so I'll be looking to that when I tackle my own views. It shouldn't be too long before I look at this mind.
Cheers,
C.
@Peter - thanks for the link, that should be useful to quite a few people.
ReplyDelete@Josh - nice!
Hi and thx for this great things,
ReplyDeleteAbout the FUDGE factor:
What about binding the Response presenter asEagerSingleton ?
Regards.
I tried a customized version of this and got the following error:
ReplyDeleteERROR] Deferred binding result type 'net.customware.gwt.dispatch.client.DispatchAsync' should not be abstract
Any ideas?
I ran into this same problem - I can't say for sure what the issue is in your case; I had neglected to include the GinModules annotation on the GreetingGinJector.
ReplyDeleteUnfortunately, you're probably going to have to diff your rewritten code against the sample project to determine the exact source of the problem.
This comment has been removed by a blog administrator.
ReplyDeleteFollowing your excellent instructions and example code here, I was able to wire in gwt-presenter and gwt-dispatch to my existing GWT + AppEngine project in less than a day. Even more importantly, it ran correctly the FIRST TIME. I can't thank you enough for putting this all together. I wanted to use MVP per the Google I/O talk, and this has made it 100x easier.
ReplyDeleteWhat was the reason behind placing gin.jar inside of root-level lib folder? After I moved this jar to standard location in "war/WEB-INF/lib" it seems that it made no difference to how application runs...
ReplyDeleteHi,
ReplyDeleteI would like to ask, how would you suggest for me to handle the history in this situation:
1. There is a MainPresenter, which presents in a DockPanel any other presenter in my application.
2. Although, it seems to me that, because the mainPresenter is always visible, the OnPlaceRequest is only called within its place. It's not called on the Presenter inside the DockPanel.
Is my use of the MVP wrong? How would you suggest me to correct this?
Thanks a lot!
Marc
Oh. Sorry, I just realized it it's in fact calling the correct OnPlaceRequest of each presenter.
ReplyDelete=)
Thanks, anyways! Your post is enlightening!
No, I am wrong again... lol
ReplyDeleteSorry to disturb.. My MVP is not functioning properly using this pattern I told you above.
I'd be glad If I could receive any suggestions.. =)
Thanks
Marc
Thank you for putting this together; it really simplified getting gin and guice running with MVP for me. Using your demo I was able to get going very quickly!
ReplyDeleteI look forward to seeing your solution for the fudge factor :)
Hi, Thanks for your work. I managed to get it working. When trying to mod it and extend it for my own use -- to add a new view, Pattern and RPC service -- I got stuck. Could someone give me an example of this? Thanks again,
ReplyDeleteJames
Excellent tutorial. Found some @Overrides that needed to be deleted, but otherwise very clean.
ReplyDeleteMinor UI tweak: GreetingResponseView extends DialogBox. If you notice in the boilerplate Greeting from Google, the last line centers the dialog box. So, after 'setWidget(dialogVPanel);' in GreetingResponseView, add 'this.center();'
Sorry for the delay in replying.
ReplyDelete@Gregory - this was just to get rid of warnings like these:
The following classpath entry 'blah' will not be available on the server's classpath
I realise that you can right click on the warnings and "quick fix" them to be ignored.
@Marcalc - if you're still having problems it's probably best that you post your questions to the gwt-presenter group:
http://groups.google.com/group/gwt-presenter
@Syntax - thanks! I haven't really come up with a better solution to the fudge factor other than maybe using asEagerSingleton() as suggested by NarZo above.
@jaga - you may need to post to the gwt-presenter (re: new view/presenter) and gwt-dispatch (re: new RPC) groups.
http://groups.google.com/group/gwt-presenter
http://groups.google.com/group/gwt-dispatch
@Stuart - thanks for the feedback.
Thanks very much for writing this.
ReplyDeleteDo you have an archive of a working sample that includes all the libraries for us to download? I am having trouble with the gin libraries.
ReplyDelete@Sujay - you can download an Eclipse project complete with dependencies from here:
ReplyDeletehttp://static1.hivedevelopment.co.uk/blog/gwt-best-practise-mvp-di-command/GreetMvp.zip
how can this be used with RequestBuilder
ReplyDelete@Randy - AFAIK RequestBuilder is not currently supported directly by gwt-dispatch. Here is a discussion might get you started:
ReplyDeletehttp://groups.google.com/group/gwt-dispatch/browse_thread/thread/b87dca66d886fdbc/cf0188239903b636?lnk=gst&q=RequestBuilder#cf0188239903b636
This comment has been removed by the author.
ReplyDelete@Oliver - sounds like you're missing the guice-2.0.jar. Make sure it's the version that ships with your version of GIN.
ReplyDeleteHi,
ReplyDeleteThat's why I removed my comment, I figured out why I get this error. But now, I got this 2 error
[ERROR] Line 57: Rebind result 'net.customware.gwt.dispatch.client.DispatchAsync' must be a class
[ERROR] Line 114: Rebind result 'net.customware.gwt.presenter.client.EventBus' must be a class
I change a bite the code to match my sample app. but I kept the declaration and usage of this two classes like in the tutorial.
This comment has been removed by the author.
ReplyDelete@Oliver - not sure what happened there, looks like some GIN config is not quite right.
ReplyDelete@Paatal - you're right - they're not needed. They were left over from the generated starter application and I forgot to remove them - my bad, sorry.
First of all thank you for all you efforts.
ReplyDeletesuppose we want to execute a command on the client side, so we need a handler implementation on the client side. it can have the undo effect too. My question is where are we supposed to bind this handler? the dispatcher is of type DispatchAsynch and I think it is only for actions that have a handler on the server.
@Meysam - AFAIK gwt-dispatch is for commands that are executed at the server. However, you may wish to get a more informed answer to this by posting on the gwt-dispatch group.
ReplyDeleteHello, everyone is interested in printable and readable version of this conference, please look
ReplyDeletehttp://extgwt-mvp4g-gae.blogspot.com/2009/10/gwt-app-architecture-best-practices.html
Hi,
ReplyDeleteI am fighting for a while on this. I get "java.lang.RuntimeException: Deferred binding failed for 'com.greeting.client.mvp.GreetingView' (did you forget to inherit a required module?)"
Do you think this can be cause by the version of Java I am using ? Do you have a war version of your code, because I am force to change your code for compilation. Does I have to specify all the template like : WidgetPresenter<GreetingPresenter.Display> this change solve a lot of issue on my side. I am not a Java specialist. Thanks for your answer.
@Oliver - a complete project is down-loadable from here:
ReplyDeletehttp://static1.hivedevelopment.co.uk/blog/gwt-best-practise-mvp-di-command/GreetMvp.zip
The "Deferred binding failed" error can sometimes come from resolving the wrong package for a class - for example I'm often caught out if Eclipse resolves the wrong kind of Log, say:
import org.mortbay.log.Log;
instead of:
import com.allen_sauer.gwt.log.client.Log;
Cheers,
Chris.
Thanks, for your answer, finaly I discover that using JDK 1.6 is working for your project without any changes :).
ReplyDeleteThen my issue was stupid I forget to add placeholder in the html page, but error was not very clear :).
regards
Olivier
This looks great and I'd like to run it from Eclipse, but 1) I get errors by many of your override annotations, which 2) if I simply remove, result in some module loading error when I launch this as a Google Web App.
ReplyDeleteBummer... I don't think this will work on OS X 10.5 with JVM 1.5 or JVM 1.6 (-d32). At least, I couldn't get it to work.
ReplyDeleteChallenges faced:
Project doesn't support JVM 1.5, so I tried JVM 1.6... and on 10.5, I don't think JVM 1.6 supports a 32 bit mode.
Anyone else successful?
I had some problems running the linked project in OSX 10.5.7.
ReplyDeleteI turns out that GWT hosted mode requires JDK 1.5 here. The problem was that gin.jar (the only lib actually compiled from source by the article authtor) was compiled with JDK 1.6.
I thought I should mention this for others on this OS.
Thanks for this code - it helped me understand. I think it would of been better if some of the unnecessary code was removed such as GreetingView.java's RootPanel.get("foo").add(name) and the GreetingService.java file.
ReplyDeleteHi, great tutorial.
ReplyDeleteSo this event bus is used only on the client gwt side to transfer event messages to the "subscribed" views?
For example if i have some custom slider and i want to update several other ui components on my screen when the slider moves this is the perfect solution? And in that way the ui components wouldn't know about the slider.
Also could you place another link for this "hupa" project because the above is broken.
And my last question for now:
Is this sample hello world project configuration and setup ready for real project usage? I asj because i do not know any thing about gin, juice, gwt-dispatch and gwt-presenter, and i am going to implement some small project with gwt in the next month and i would like to try this architecture, but i am afraid that i will not have much time to investigate in the above libraries.
Regards and again great work.
I have created a skeleton app, for anybody who want's to save few minutes. It uses all the above stuff, but instead of Javish serialized RPC, it uses Restlet GWT library, and jruby + Sinatra backend.
ReplyDeletehttp://github.com/skrat/sinwar
@bot - sorry for late reply.
ReplyDelete> So this event bus is used only on the client gwt side to transfer event messages to the "subscribed" views?
Yes.
> For example if i have some custom slider and i want to update several other ui components on my screen when the slider moves this is the perfect solution? And in that way the ui components wouldn't know about the slider.
Yes exactly.
> Also could you place another link for this "hupa" project because the above is broken.
This project was moved a short while ago to here:
http://james.apache.org/hupa/index.html
>And my last question for now:
>Is this sample hello world project configuration and setup ready for real project usage? I asj because i do not know any thing about gin, juice, gwt-dispatch and gwt-presenter, and i am going to implement some small project with gwt in the next month and i would like to try this architecture, but i am afraid that i will not have much time to investigate in the above libraries.
I used it for my project and can recommend the MVP architecture. As various comments above point out, there are some old artifacts lying around the project which can be deleted. Please be advised that the gwt-presenter project is undergoing a bit of an overhaul at the minute so my code might need adjusting if you upgrade the libraries. That said - you should still be able to get a lot done with the version supplied with the project.
All the best,
Chris.
@killallhumans - neat!
ReplyDelete// NOTE: the servlet context will probably need changing
ReplyDeleteserve("/greetmvp/dispatch").with(DispatchServiceServlet.class);
I don't really understand these code lines.
Where can I configure that the RPC commands sent from the client is sent to "/greetmvp/dispatch"?
Should that be configured in the web.xml file?
Hi all,
ReplyDeleteI've applied gwt-presenter with a server side spring support for spring-security at method level.
Code can be found at http://code.google.com/p/orcades-gwt-spring/
Please comment.
The documentation is short, but fluent maven user should make it.
This comment has been removed by the author.
ReplyDeleteChris,
ReplyDeleteThanks for the code, I got it to work with some minor modifications (see below).
1) Used JDK 6
2) Removed @override annotations as per Eclipse's recommendation in a few places.
3) Removed the classic RPC classes such as GreetingService.java, GreetingServiceAsync.java, GreetingServiceImple.java and SendGreetingHandler.java (under sitealright package).
Curious, have you tried to implement this using SmartGWT yet, we might see more use of this then.
Hi.
ReplyDeleteJust wanted to thank you guys for this walkthrough. I have been using it extensively while planning my own project which I describe on http://borglin.net/gwt-project
It looks like MVP is a good idea in theory, but it clearly turns simple tasks into monolithic implementations. Look this sample MVP project here. A simple CRUD operation on a "task" becomes 16 classes. I'm sold on the idea. Not sold on the pain it takes to make this happen.
ReplyDeleteoops. forgot the link. This is a similar sample project as the one described above:
ReplyDeletehttp://code.google.com/p/gwt-mvp-sample/
Hi
ReplyDeleteI habe been getting the following errors while compiling the client code. Can anyone help?
Compiling module co.uk.hivedevelopment.greet.GreetMvp
[ERROR] Errors in 'generated://D4E46AF2E7AE7489E374F22EA6E3A89C/co/uk/hivedevelopment/greet/client/gin/GreetingGinjectorImpl.java'
[ERROR] Line 19: Rebind result 'co.uk.hivedevelopment.greet.client.mvp.GreetingPresenter.Display' must be a class
[ERROR] Line 34: Rebind result 'co.uk.hivedevelopment.greet.client.mvp.GreetingResponsePresenter.Display' must be a class
[ERROR] Line 91: Rebind result 'com.google.gwt.user.client.ui.HasWidgets' must be a class
[ERROR] Line 144: Rebind result 'net.customware.gwt.presenter.client.EventBus' must be a class
[ERROR] Line 159: Rebind result 'net.customware.gwt.dispatch.client.DispatchAsync' must be a class
[ERROR] Cannot proceed due to previous errors
@Hang - not sure about that error, I'd have to see some code. Have you downloaded the ready-to-go eclipse project of this example?
ReplyDeletehttp://static1.hivedevelopment.co.uk/blog/gwt-best-practise-mvp-di-command/GreetMvp.zip
Cheers,
Chris
Thank you very much for this tutorial but i want to know why have you put the instantiation of "GreetingResponsePresenter" in the constructor of GreetingPresenter and not in the constructor of AppPresenter?
ReplyDeleteThis comment has been removed by the author.
ReplyDeleteGood tutorial but this:
ReplyDelete@Override
30. protected void dispatch(final GreetingSentEventHandler handler) {
31. handler.onGreetingSent(this);
32. }
will not work since Overiding this method requires EventHandler rather than GreetingSentEventHandler
Greetings from http://AngloPolish.com
hi,
ReplyDeleteis there any way of achieving some way of code splitting here? The problem i see is in the fact that all view/presenters bindings take place in GreetingClientModule class. This will inevitable have a significant impact on the startup/download time of even medium size application. Is there any way of incorporating something like http://code.google.com/webtoolkit/articles/mvp-architecture-2.html#code_splitting
GWT.runAsync(new RunAsyncCallback() {
...
public void onSuccess() {
...
}
}
by the way, very good article.
Hi,
ReplyDeleteI have a question concerning the DialogBox. How the DialogBox know, that it should appear? In other words: Which event triggers the appearance of this DialogBox?
In GreetingPresenter, you fire a GreetingSentEvent. And in GreetingResponsePresenter you add a handler for the GreetingSentEvent to the eventBus.
But what are the steps in between? From where will be the GreetingResponsePresenter called?
@greg
ReplyDeleteI've done code splitting with gwt-presenter. It's a bit much to write about here in the comments, but I was able specify a module:
public class Module extends AsyncModule\
{
@Inject
public Module(final Provider value)
{
super(value);
}
}
Where UserWindowPresenter is a presenter that has a few sub presenters relating to a particular business area.
Elsewhere, I then show the above presenter by having something like:
public class PresenterRegistry
{
private Provider userWindowPresenterProvider;
@Inject
public void setUserManagementPresenter(final Provider provider)
{
userWindowPresenterProvider = provider;
}
private void revealUserManagementWindow()
{
GWT.runAsync(new RunAsyncCallback()
{
@Override
public void onFailure(final Throwable reason)
{
}
@Override
public void onSuccess()
{
final Presenter presenter = userWindowPresenterProvider.get();
presenter.revealDisplay();
}
});
}
}
I then call into the presenter registry to show a form.
I hope that helps. I'd like to find some time to write this up properly on the blog in the very near future, so watch this space.
Blogger has scrubbed generics from the code snippet I just posted. :(
ReplyDelete@seakey
ReplyDeleteNothing too clever I'm afraid. GreetingResponsePresenter defines this:
@Override
protected void onBind() {
...
eventBus.addHandler(GreetingSentEvent.TYPE, new GreetingSentEventHandler() {
@Override
public void onGreetingSent(final GreetingSentEvent event) {
...
display.getDialogBox().show();
}
});
}
Obviously it's the call to .show() that causes the display of the dialog box. The code block is a handler for a GreetingSentEvent which is fired when a server call returns successfully:
public class GreetingPresenter extends WidgetPresenter {
...
private void doSend() {
...
dispatcher.execute(new SendGreeting(display.getName().getValue()), new DisplayCallback(display) {
...
@Override
protected void handleSuccess(final SendGreetingResult result) {
// take the result from the server and notify client interested components
eventBus.fireEvent(new GreetingSentEvent(result.getName(), result.getMessage()));
}
});
}
@Chris: Yeah, that's clear. I wanted to know, where the GreetingResponsePresenter will be triggerd. But I believe, that I have found the point: It's because of the
ReplyDeletebindPresenter(GreetingResponsePresenter.class, GreetingResponsePresenter.Display.class, GreetingResponseView.class);
in GreetingClientModule, right?
Thanks for your answer.
Hello
ReplyDeleteThanks for a great guide.
I have a problem with the changing of presenters/views.
I have a LoginPresenter/LoginView when i login i want to change the presenter/view to StartPresenter/StartView.
Where and how should i do this?
Thanks again
/Filip
This comment has been removed by the author.
ReplyDeleteHi,
ReplyDeleteFirst thanks for this excellent tutorial! This incredibly helps to jump start.
It looks like the code is no longer compatible with the 2.04 GWT environment as I get the following ERROR which breaks the app.
Using SDK 2.0.4, Eclips (latest) and Java 1.6 on OSX
It starts with a warning:
14:56:04.924 [WARN] [greetmvp] Line 41: Referencing deprecated class 'com.google.gwt.user.client.rpc.SerializableException'
[INFO] Calling doSend
[ERROR] Handle Failure:
14:56:30.248 [ERROR] [greetmvp] 2010-07-24 14:56:30,247 [ERROR] Handle Failure:
com.google.gwt.user.client.rpc.IncompatibleRemoteServiceException: Type 'co.uk.hivedevelopment.greet.shared.rpc.SendGreeting' was not assignable to 'com.google.gwt.user.client.rpc.IsSerializable' and did not have a custom field serializer. For security purposes, this type will not be deserialized.
Now to fix this weired error, remove the gwt-sevelet.jar from the WEB-INF/lib dir and things will work again.
Wessel
Hi,
ReplyDeleteDo you check http://code.google.com/p/guit/ ?
I think Guit have the same structure with this example.
So sorry, I'm a new comer. I don't understand a lot of thing. Please help me.
I want to use this architecture combine with GAE's datastore (using JDO)
http://code.google.com/p/diaryoftour/
I will learn more. Hope any body can help me :)
I'm still getting the same error with 2.0.4 and 1.5 JRE...
ReplyDeleteI tried removing gwt-servlet.jar and it didn't work... any clues
Hi,
ReplyDeleteGreat job. Do you think that you can post an integration with Spring framework on the backend ? It seems that i can use Sping MVC or Spring4gwt (not supported cause Google Gin), Google Gin, GWT-SL, ... There is also Spring Roo (I didn't have a look at it).
Thanks
JMi
yea um, this article is almost too old to be worth using.
ReplyDeleteWish OP would submit some updates for 2010...
I've managed to integrate gwt-presenter & dispatch, as well as work up a decent smart-gwt interface. Once I get it rock solid, I'll post it here by some means.
I feel compelled to share it, after working an entire day getting old code from 2009 to work.
I doubt that the above article even repesents an accurate "standards of practice" anyway. GWT has changed alot.
Now I know why Java's logo is a coffee cup..
it's the same as other things early in the morning that you hate to deal with, but do so anyway cause your cracked out on caffeine and lusted for money.
veterans day quotes
Deleteveterans day poems
veterans day memes images
veterans day parade
veterans day clip art facebook
veterans day pictures
Veterans Day images
Jared,
ReplyDeleteSorry to hear that you had trouble getting this code working. I'll try to post a link to an updated snapshot based on the latest releases of the gwt-presenter/gwt-dispatch libraries. Is that the update you are referring to? If you let me know your particular pain points then I'll try to address them.
As with many blog posts, what was written above was correct (to the best of my knowledge) at the time of writing and, indeed, a year is a relatively long time with a project evolving as rapidly as GWT. In fact GWT 2.1 will provide its own MVP library potentially removing the need for gwt-presenter. It seems harsh to call me on the fact that the code is now out of date - that's just life in the technical blogsphere.
When I use the term "best practice", it is not self proclaimed but taken from a Google I/O 2009 talk which discusses a best practice approach to developing GWT projects which crucially means employing MVP, Command and Dependency Injection patterns. I believe this post successfully demonstrates the setup and interoperation of the required libraries to use the patterns.
Regards,
Chris.
Hi,
ReplyDeleteYou might like to take a look at gwt-platform (http://code.google.com/p/gwt-platform/ and http://uptick.com.au/content/getting-started-gwt-platform).
Cheers
Mark
Even if this is old, it still looks like a great guide to get started!
ReplyDeleteQuestion: I don't see any license restrictions on any of this code. Have you released this into the public domain?
@Tom,
ReplyDeleteThanks! Yes the code is public domain.
Cheers,
Chris.
Awesome! My manager says we can put a link to here in our license header and give you props. You probably saved me a week of work.
ReplyDeleteWould you by any chance have any recommendations on widget libraries? Some of them have some cool features we could really use, but seem to force you into the MVC pattern.
I've worked with Sencha Ext GWT (a.k.a GXT) a lot over the last 9 months or so.
ReplyDeletehttp://www.sencha.com/products/gwt/
It's a rich library and can be adapted to fit in with MVP but it can take some work. For instance, at a basic level you'll need to make components adhere to HasValue interfaces and so on. Those sorts of changes are pretty quick. More advanced components like trees, grids and combos need more effort.
All in all, we've got some very impressive results using this library.
Cheers,
C.
great explanation and example.
ReplyDeleteThanks a lot it helped me to learn and implement.....
Regards,
Dew
@Chris,
ReplyDeleteAny progress on the solution you are working on to get rid of the fudge factor?
Danny
HI Thanks for your tutorial. It works as miracle. Though i had a small question. How do we change the call from /greetmvp/dispatch to anything that we want like /greetmvp/checkthis and how do we call this???
ReplyDeleteThanks again.
Thanks for the information
ReplyDeletehttp://extreme-java.blogspot.com
Hi. Thanks for the tutorial - a great effort. I downloaded the complete project as a zip (as suggested in the article) - it compiled, but the RPC failed. The error message was "Application out of date ... expecting version 6, got 5 ...". Turned out I just had to replace gwt-servlet.jar (2009) with the latest version (2011).
ReplyDeleteHi when I am trying to deploy the greetMVP example in my process server I am getting with Multiple Servlet injectors detected. This is a warning indicating that you have more than one GuiceFilter running in your web application. Can you help me out in this.
ReplyDeleteThanks for this nice article. would be great if you could answer my question as well I have been in Java swing development for few years and now I see GWT getting popular , though I haven't used GWT , what are differences between GWT and Swing and how much learning curve for a swing developer to learn GWT.
ReplyDeleteThanks
Javin
How Garbage collection works in Java
This is excellent stuff man you have doe a pretty good job on explaining things. thanks a lot. set java classpath
ReplyDeletehi have exception , please clarify this. why this happened....
ReplyDeleteEntryPoint initialization exception
Exception while loading module fr.micouz.gwt.dispatch.client.Gwt_dispatch_example. See Development Mode for details.
com.google.gwt.core.ext.UnableToCompleteException: (see previous log entries)
at com.google.gwt.dev.shell.ModuleSpace.rebindAndCreate(ModuleSpace.java:503)
at com.google.gwt.dev.shell.ModuleSpace.onLoad(ModuleSpace.java:375)
at com.google.gwt.dev.shell.OophmSessionHandler.loadModule(OophmSessionHandler.java:200)
at com.google.gwt.dev.shell.BrowserChannelServer.processConnection(BrowserChannelServer.java:525)
at com.google.gwt.dev.shell.BrowserChannelServer.run(BrowserChannelServer.java:363)
at java.lang.Thread.run(Unknown Source)
Fist of all, your project help me to understand MVP and GWT.
ReplyDeleteBut i still do not succeed on launching the application. I have the problem:
13:49:39.829 [ERROR] [greetmvp] Failed to create an instance of 'co.uk.hivedevelopment.greet.client.GreetMvp' via deferred binding
java.lang.RuntimeException: Deferred binding failed for 'co.uk.hivedevelopment.greet.client.gin.GreetingGinjector' (did you forget to inherit a required module?)
Do you have any idea of the answer to that.
Thanks
Someone found a solution to the last question because I have the same problem.
ReplyDeletethanks
Thanks for the demo. This demo did one thing in particular that I couldn't find anywhere else out there- couple the MVP pattern with a DialogBox. Simple enough sounding but not so simple to see through, for me. Reason: my MVP presenter/views had all been hooked up to initialize/construct in response to events (similar to Contacts Demo) from the controller. Whereas in this case we need to instantiate the presenter/view in the bind method of the controller, so that we can have the presenter then listen for the event from the bus to then show itself. Wordy I know but I'm happy so oh well. Awesome.
ReplyDeleteburberry outlet online
ReplyDeleteralph lauren outlet
coach outlet
cheap oakleys
canada goose jackets
cheap oakley sunglasses
air force 1
coach purses
louis vuitton outlet
coach factory outlet
running shoes
rolex watches uk
babyliss flat iron
cheap jordan shoes
coach factory outlet online
polo ralph lauren
polo ralph lauren
cheap jordans
oakey sunglasses wholesale
louis vuitton pas cher
air max 90 black
birkenstock outlet
asics gel nimbus
coach outlet store
the north face outlet
michael kors outlet clearance
nike tn
rolex replica watches
nike air max
true religion outlet store
wholesale nike shoes
polo ralph lauren
kobe bryant shoes
louis vuitton outlet online
lacoste outlet
louboutin shoes
louis vuitton outlet online
red bottoms outlet online
abercrombie outlet
2016614yuanyuan
This is one of the cult game now, a lot of people enjoy playing them . Also you can refer to the game :
ReplyDeletegold mine strike | pokemon go 2
The game controls are shown just under . Movement mechanisms primarily include acceleration and tilting controls.
stickman games | stick war 2 | animal jam 2
android apk installer download free..
ReplyDeletebest lenovo laptop under 50000
ReplyDeletebest lenovo laptop under 60000
mobile imei number ussd code
reliance 2G 3g internet balance check
mobile under 13000
mobile under 14000
idea number check
Deleteidea number check code
idea number check code
mobile below rs 1000
pandora charms
ReplyDeletetrue religion jeans
birkenstock sandals
adidas uk
beats by dr dre
chicago cubs jerseys
michael kors
louis vuitton canada
adidas yeezy boost 350
cheap nfl jerseys
0509shizhong
Tomorrow is the last entry day. To all of the wonderful people whom I have had commented on their remarks and their comment's on mine. I thank you. I think the Blogs are great and what learning experiences we all get from each other's views. A true learning experience. I wish all of you best of luck, I will be envious if I don't win but such is life. Regards osappsbox
ReplyDeleteWonderful to share
ReplyDeletePassword protect folder in windows
Very nice article thanks for sharing this beautiful post i also have my Best Gaming Mouse Site
ReplyDeleteThanks for the marvelous posting! I really enjoyed reading it; you might be a great author. I will always bookmark your blog and definitely will come back very soon. I want to encourage you to continue your great writing, have a nice day! download free android apks.
ReplyDeleteJust desire to say your article is as astounding. The clarity in your post is simply cool and i can assume you are an expert on this subject. Thanks a lot for sharing.
ReplyDeleteMywegmansconnect
Mywegmansconnect
great post Ymail
ReplyDeleteread the login steps of Mywegmansconnect mywegmansconect
ReplyDeleteThis blog is so nice to me. I will continue to come here again and again. Visit my link as well. Good luck
ReplyDeletecara menggugurkan kandungan
cara menggugurkan kandungan
cara menggugurkan kandungan
Whatsapp Group Names
ReplyDeleteAdd Bitmoji Keyboard Android
ralph lauren outlet
ReplyDeleteugg boots clearance
ugg boots clearance
nfl jerseys wholesale
adidas outlet store
lebron shoes
pandora outlet
ugg boots
adidas outlet online
true religion outlet
yaoxuemei20171222
A good site with exciting content that is what I need. Thank you for making this web site, and I will be visiting again. It is really resourceful blog with great content. regards apksfile
ReplyDeletehttps://moviehdapkz.com/
ReplyDeleteGreat post thanks for sharing. sms bomber, flappy bird apk, whatsApp plus
ReplyDeletevery nice article and helpful to everybody if you want to check about mobile IMEI Number information visit here
ReplyDeleteHow to check Mobile IMEI Number
polo ralph lauren outlet online
ReplyDeletekate spade
moncler
coach outlet
coach factory outlet
ralph lauren outlet
pandora charms
pandora charms
philipp plein outlet
nmd adidas
clb0309
Thanks for sharing this marvelous post. I m very pleased to read this article.Belkin setup
ReplyDeletegreat post...if you looking for best ddr4 ram
ReplyDelete
ReplyDeleteBefore you plan to install antivirus software on your device, you are required to take
few important steps to avoid software conflicts with the previously installed versions. please visit
norton activation code
you can find more topics there.We love to help you.
Thanks for helping us by providing unique things,that is somewhere helping us in our life.
ReplyDeleteIf you stuck with pogo issues.
Fix that with pogo customer support.
It just for you and your product safty.
At first it may appear to be overwhelming (and it will take some time and imagination), however split up your battles.for more information
ReplyDeleteGoogle PPC might be one of the most straightforward approaches to produce movement to your site and make some respectable benefits from your AdWords crusade..40 High Domain And Page Authority
ReplyDeletenorton.com/setup - Online Help – Step by Step guide for Norton Setup, Download & complete installation online. We are providing independent support service if in case you face problem to activate or Setup Norton product.
ReplyDeleteOffice.com/setup – Microsoft Office is a set of several desktop applications which helps to perform various tasks simultaneously. Microsoft has stopped designing the Office Setup 2007 as it is the old version of Microsoft Office. You can visit office.com/setup for the Office Setup download or Office Setup Activation.
ReplyDeleteoffice.com/setup - Let's Get Started with your Microsoft Office Setup by visiting www.office.com/setup and Enter 25-digit Activation Code to Setup Office Today. Feel free to contact us for any help.
ReplyDeleteoffice.com/setup, office.com/verify, www office com setup, office.com/myaccount, Office Com Setup, office.com/productkeycard, Office Setup, setup.office.com, Office Com Set Up, office 2017 download, Office Setup Enter Product Key, office 2016 download, Office Setup Enter Key, office for mac download, Office Com Setup Enter Key
ReplyDeleteOnline Supports...
ReplyDeleteTurboTax Support
Quicken Support
Quickbooks Support
Mobile USSD Codes
ReplyDeleteJio USSD Code
jio sim port to other number
jio balance transfer code
check jio number
jio loan number code
jio internet data usage
jio toll caller
jio caller tune number
jio dnd start
This is good website to display.
ReplyDeletePort aircel number
This comment has been removed by the author.
ReplyDeleteInterested in joining the Customer Panel? frontline. Not yet a member of the Kroger Family of Stores Customer Panel? You can provide feedback on your shopping experiences. Just click here and complete a brief online feedback survey to join the panel. As a member, you'll receive more survey invitations periodically by email.Check this url: https://krogerguide.yolasite.com/
ReplyDeletekeep it really nice it..
ReplyDeleteDownlaod 3DS Emulator For PC and Android
Spotify Premium Apk Free Downlaod [Latest new Version]
Downlaod : The Best SNES Emulator For Windows
how to share data in airtel
ReplyDeletehow to share airtel data
how to share airtel data
click here
how to share data in airtel
Really i found this article more informative, thanks for sharing this article.
ReplyDeleteflappy bird apk
mxplayer
peggo apk
Bollywala.com
ReplyDeleteSunny Leone Sexy Photos
Sunny Leone Hot Bikini Wallpapers
urvashi rautela bikini
katrina kaif bikini
priyanka chopra bikini
Sunny Leone Bikini
kajal agarwal bikini
kareena kapoor bikini
Downlaod PS4 Emulator For PC
ReplyDeleteDownlaod 3DS Emulator For PC and Android
Xbox One Emulator For Windows
This comment has been removed by the author.
ReplyDeleteAmazon Echo Dot Review webbywebb
ReplyDeleteCool Gadgets under $400 for Men
Seagate Backup Plus 4TB Portable External Hard Drive
Our technical experts will offer you the help to download, install and reinstall the McAfee product on your system. McAfee also provides the support to resolve the key related errors as well as the issues related to system performance.
ReplyDeletemcafee.com/activate
Office.com/setup – Let’s Get Started with Office Setup Installation by simply following the steps discussed below. You may easily activate Office with the help of Office Com Setup link.
Office.com/setup
Thank you for bringing more information to this topic for me. I’m truly grateful and really impressed. Download Full Music Albums for Free
ReplyDeleteBollywala.com
ReplyDeleteSunny Leone Sexy Photos
Sunny Leone Hot Bikini Wallpapers
urvashi rautela bikini
katrina kaif bikini
priyanka chopra bikini
Sunny Leone Bikini
kajal agarwal bikini
kareena kapoor bikini
vans outlet
ReplyDeletecolumbia
replica rolex watches
stuart weitzman
ray ban sunglasses
polo outlet
jordan 3s
shoe carnival
light up shoes
supreme
chenyingying20180707
Wow that's great also checkout
ReplyDeleteextratorrent proxy
Thanks for sharing this beneficial information
ReplyDeletehotmail login
Jio Giga Fiber booking
ReplyDeletejio giga fiber
Jio Gigafiber price
Jio Fiber Dealership
Jio GigaFIber Plans
Vodafone Balance Transfer
ReplyDeleteVodafone Unlimited Calling Plan
Vodafone SMS Pack
Voafone Caller Tune
Vodafone Internet settings
Vodafone DND Services
Vodafone Loan Number
Vodafone Customer Care Numbers
Port Vodafone Number
longchamp pas cher
ReplyDeletepolo ralph lauren
nfl jerseys
nike air max 90
guess factory
chloe handbags
adidas superstar
designer sunglasses
ray ban occhiali
mizuno running shoes
2018.7.21xukaimin
Online Supports...
ReplyDeleteoffice.com/setup
office.com/setup
norton.com/setup
office.com/setup
hp support
TurboTax Support
ReplyDeleteQuicken Support
Quickbooks Support
TuneUp PC Performance AVG – Be it a new computer or an old one, at one point the user feel that the device is not working or lagging while working with the application. To solve this issue faced by many users, AVG has a Product AVG PC TuneUp that helps your device to perform better. It has additional features that free your RAM and Secondary Storage(Hard Disk) as well.TuneUp PC Performance AVG
ReplyDeleteNorton Antivirus Update Stuck – Norton Antivirus is essential for your devices to protect your digital life. In order to keep your digital life safe, you can download and install Norton Antivirus on your system. Here in this article, we are going to discuss the process to Fix Norton Antivirus Update Stuck.
ReplyDeleteNorton Antivirus Update Stuck
Damage Control Tips After Being Hacked – A hacker can access your personal, professional information along with your debit/credit card details. By using your social media accounts, a hacker can spread malware or ransomware to your contact by pretending to be you. If you have been hacked then you need to follow some tips for the damage control. Here in this article, we are going to discuss the Damage Control Tips After Being Hacked.
ReplyDeleteDamage Control Tips After Being Hacked
love shayari in english
ReplyDeleteairtel ussd codes
ReplyDeletecheck airtel net balance
vodafone ussd codes
bsnl ussd codes
mtnl ussd codes
Vodafone store near me
Vodafone Net Balance Check
vodafone caller tune
Aadhar Card Download by Name and DOB
ReplyDeleteAadhar Card Download by Name and DOB
Aadhar Card Download by Date of Birth
Aadhar Card Download by Date of Birth
Aadhar Card Download by Name
lucky patcher download
ReplyDeleteCheck this for entertainment Heraldclub
ReplyDeleteGreat information about Best Screenshot Software
ReplyDeleteugg outlet
ReplyDeletenfl jerseys wholesale
michael kors outlet
true religion outlet
michael kors outlet
michael kors outlet
coach outlet online
coach outlet
michael kors outlet clearance
nike outlet
yaoxuemei20180820
lebron james jersey
ReplyDeletejordan 5
nike air force 1
nike air max
hermes bags
hermes bags
nfl jerseys
police sunglasses
bcbg
nike factory
chenyingying20180904
Happy Veterans Day 2018
ReplyDeleteGet started office Setup with Product Key at www.office.com/setup. Sign in, Enter Product Key and install Office or call us toll-free +1-888-406-4114.
ReplyDeletehey nice tanku for sharing wonderfull information for more tech news
ReplyDeletehey nice aritcle tankue for sharing for more travell news
ReplyDeleteBest Web Hosting for Small Business
6 Youtube Video Ideas if you are planning to start a Youtube channel
latest sarkari naukri notifications
latest govt jobs
government jobs coaching, competitive exam coaching
free sarkari job alert
ap govt jobs
up govt jobs
10th pass govt job
12th pass govt job
Thanks for sharing this post, it was great reading this article! would like to know more! keep in touch and stay connected! Also Check here
ReplyDeleteShowBox App Free Download
gbwhatsapp 6.55
GbWhatsapp Free Download
Mini Militia Pro Pack
Best Apk Mods
Smm Panel
Just Smm
Panchtantra Ki Kahani
ReplyDeletePanchtantra Ki Kahani In Hindi
hindi panchatantra stories
Hindi Moral Stories
story in hindi for kids
Hindi Short Story
Moral Stories in Hindi
Story In Hindi
ReplyDeleteihappyveteransdayimages.net
jordan 5
ReplyDeleteferragamo sunglasses
armani sunglasses
nike mercurial
adidas clothing
sac michael kors
bottega veneta
germany world cup jersey
occhiali oakley
cheap jerseys
2018.10.6xukaimin
Fildo apk
ReplyDeleteTraffic Rider MOD APK
ReplyDeleteMacOS Sierra ISO Download
MacOS X Lion Download
Very Nice Website Veterans Day Meme 2018
ReplyDeleteThanks for this post,and for the code too. I've implement this on my domain Torpidox
ReplyDeleteThanks For Share this artical your website is amazing Veterans Day 2018
ReplyDeleteVehicle Ownership Transfer Procedure in Kerala
ReplyDelete