User Tools
Writing /app/www/public/data/meta/archive/blog.cacoethes.co.uk/gwt-commandpattern.meta failed
Unable to save metadata file. Hint: disk full; file permissions; safe_mode setting.
archive:blog.cacoethes.co.uk:gwt-commandpattern
Differences
This shows you the differences between two versions of the page.
| archive:blog.cacoethes.co.uk:gwt-commandpattern [2018/02/22 14:02] – created wlee | archive:blog.cacoethes.co.uk:gwt-commandpattern [2021/06/25 10:09] (current) – external edit 127.0.0.1 | ||
|---|---|---|---|
| Line 1: | Line 1: | ||
| + | This content is copied from http:// | ||
| + | ====== The Command Pattern with the Grails GWT Plugin ====== | ||
| + | A talk by Ray Ryan of Google at the 2009 Google IO conference has caused a bit of a buzz in the GWT community. In that talk, Ray recommends several design patterns that will help make your projects more manageable as they grow in size. The particular suggestion I’ll be focusing on in this post concerns using the Command Pattern for server requests. | ||
| + | |||
| + | The idea is to encapsulate the stuff you want the server to do for your client in command or action objects instead of using standard service methods. For example, rather than calling a search() method on a remote service, you have a search object that gets sent to the server and the server returns the corresponding result or response object. By using this pattern, you can effectively implement caching, batching, undo/redo, etc. See Ray’s presentation for a bit more info on this. | ||
| + | |||
| + | One ready-made implementation of the pattern for GWT is the [[https:// | ||
| + | |||
| + | ===== Getting started ===== | ||
| + | Using the framework is simplicity itself. Let’s say you want to implement a search action. You first create the appropriate classes for it by running a new Grails command: | ||
| + | |||
| + | < | ||
| + | grails create-gwt-action org.example.myapp.Search | ||
| + | </ | ||
| + | This will create the following files: | ||
| + | |||
| + | * src/ | ||
| + | * src/ | ||
| + | * grails-app/ | ||
| + | The action and response classes represent the request and response data respectively. Both of them implement Serializable, | ||
| + | |||
| + | The action handler is an instance of a Grails artifact type added by the plugin. Its role is to process actions and return the appropriate responses. It would typically offload the majority of the work to a standard Grails service that could then be used by other code, such as a REST or web services interface. | ||
| + | |||
| + | ===== The code ===== | ||
| + | |||
| + | **Update** I have updated the plugin packages in the following examples, so “org.grails.” is now just “grails.” The change was made for the final release of version 0.5. | ||
| + | |||
| + | The action object should contain only the information necessary to complete the request. Remember, it’s a data object and typically won’t have any behaviour. In the case of our search action, we might need a query string and the maximum number of results the search should return: | ||
| + | |||
| + | <code java> | ||
| + | package org.example.myapp.client; | ||
| + | |||
| + | import grails.plugins.gwt.client.Action; | ||
| + | |||
| + | public final class SearchAction implements Action< | ||
| + | private static final long serialVersionUID = 1L; | ||
| + | |||
| + | private String query; | ||
| + | |||
| + | private SearchAction() {} | ||
| + | |||
| + | public SearchAction(String query) { | ||
| + | this.query = query; | ||
| + | } | ||
| + | |||
| + | public String getQuery() { | ||
| + | return query; | ||
| + | } | ||
| + | } | ||
| + | </ | ||
| + | Likewise, the response should only contain the data required by the client: | ||
| + | |||
| + | <code java> | ||
| + | package org.example.myapp.client; | ||
| + | |||
| + | import java.util.ArrayList; | ||
| + | import java.util.Collections; | ||
| + | import java.util.List; | ||
| + | |||
| + | import grails.plugins.gwt.client.Response; | ||
| + | |||
| + | public class SearchResponse implements Response { | ||
| + | private static final long serialVersionUID = 1L; | ||
| + | |||
| + | private ArrayList< | ||
| + | |||
| + | private SearchResponse() {} | ||
| + | |||
| + | public SearchResponse(List< | ||
| + | this.ids.addAll(ids); | ||
| + | } | ||
| + | |||
| + | public List< | ||
| + | return Collections.unmodifiableList(this.ids); | ||
| + | } | ||
| + | } | ||
| + | </ | ||
| + | All very straightforward so far. The final step is to implement the action handler. As I said, this typically offloads the work to a service and in our example we will make use of a search service: | ||
| + | |||
| + | <code java> | ||
| + | package org.example.myapp | ||
| + | |||
| + | import org.example.myapp.client.SearchAction | ||
| + | import org.example.myapp.client.SearchResults | ||
| + | |||
| + | class SearchActionHandler { | ||
| + | SearchService searchService | ||
| + | |||
| + | SearchResponse execute(SearchAction action) { | ||
| + | def results = searchService.executeSearch(action.query) | ||
| + | return new SearchResponse(results) | ||
| + | } | ||
| + | } | ||
| + | </ | ||
| + | As you can see, all the action needs to do is unwrap the action and create the response. Everything else is handled by the search service. We haven’t even needed to expose a service via GWT! That’s all you need to do on the server side, which just leaves the question of how you execute an action from the client. | ||
| + | |||
| + | ===== On the client ===== | ||
| + | |||
| + | The plugin comes with a dedicated GWT RPC service that you can use to dispatch all your actions: GwtActionService. All you have to do in your GWT code is create the service, instantiate your action, and then call the execute() method: | ||
| + | |||
| + | <code java> | ||
| + | ... | ||
| + | import grails.plugins.gwt.client.GwtActionService; | ||
| + | import grails.plugins.gwt.client.GwtActionServiceAsync; | ||
| + | ... | ||
| + | protected void doSearch(String query) { | ||
| + | GwtActionServiceAsync service = GWT.create(GwtActionService.class); | ||
| + | ((ServiceDefTarget) service).setServiceEntryPoint(GWT.getModuleBaseURL() + " | ||
| + | |||
| + | service.execute(new SearchAction(query), | ||
| + | public void onFailure (Throwable caught) { | ||
| + | ... | ||
| + | } | ||
| + | |||
| + | public void onSuccess (GetRecentResponse result) { | ||
| + | ... | ||
| + | } | ||
| + | }); | ||
| + | } | ||
| + | </ | ||
| + | You could even wrap the action service with another implementation that does caching and/or batching of commands. | ||
| + | |||
| + | If you use Google Gin, you can simplify your code by having the action service injected into the classes that need it. Simply add something like this to your Gin module definition: | ||
| + | |||
| + | <code java> | ||
| + | ... | ||
| + | import grails.plugins.gwt.client.GwtActionService; | ||
| + | import grails.plugins.gwt.client.GwtActionServiceAsync; | ||
| + | |||
| + | public class MainModule extends AbstractGinModule { | ||
| + | protected void configure() { | ||
| + | ... | ||
| + | } | ||
| + | |||
| + | @Provides @Singleton | ||
| + | GwtActionServiceAsync provideGwtActionService() { | ||
| + | GwtActionServiceAsync service = GWT.create(GwtActionService.class); | ||
| + | ((ServiceDefTarget) service).setServiceEntryPoint(GWT.getModuleBaseURL() + " | ||
| + | return service; | ||
| + | } | ||
| + | } | ||
| + | </ | ||
| + | Since GwtActionService is provided by the plugin, you also need to configure your module to inherit from the plugin’s (new) module: | ||
| + | |||
| + | <code java> | ||
| + | < | ||
| + | ... | ||
| + | < | ||
| + | ... | ||
| + | </ | ||
| + | </ | ||
| + | I think this is a really clean approach to GWT RPC that reduces the burden on you as a developer. In particular, you no longer have to implement GWT services because you can create simple action handlers instead. Before I implemented this framework, I found that I was creating specific GWT versions of each service. So for example, I had a SearchService and a GwtSearchService, | ||
| + | |||
| + | I hope you like the new approach! | ||