The project was build with Maven and is available via GIT https://github.com/poseidonjm/basic-crud
If you don't have Egit you can install Help -> Install New Software...
http://download.eclipse.org/egit/updates
I am using Eclipse Java EE IDE for Web Developers.
You should install Maven http://maven.apache.org/download.html download apache-maven-3.0.4-bin.zip
Install maven plugin Help -> Install New Software...
m2e-wtp - http://download.jboss.org/jbosstools/updates/m2eclipse-wtp
Final Project
Database Table
I am using postgreSQL 8.4
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
create table colaboradores( | |
id bigserial primary key, | |
nombres varchar(250), | |
apellidos varchar(250), | |
edad int, | |
version int | |
); |
Project Structure
General POM.xml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="UTF-8"?> | |
<project | |
xmlns="http://maven.apache.org/POM/4.0.0" | |
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> | |
<!-- POM file generated with GWT webAppCreator --> | |
<modelVersion>4.0.0</modelVersion> | |
<groupId>com.example</groupId> | |
<artifactId>crud-example</artifactId> | |
<packaging>war</packaging> | |
<version>1.0-SNAPSHOT</version> | |
<name>GWT Maven Archetype</name> | |
<properties> | |
<!-- Convenience property to set the GWT version --> | |
<gwtVersion>2.4.0</gwtVersion> | |
<!-- GWT needs at least java 1.5 --> | |
<webappDirectory>${project.build.directory}/${project.build.finalName}</webappDirectory> | |
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | |
</properties> | |
<repositories> | |
<repository> | |
<id>sencha-gpl-release</id> | |
<name>Open source licensed Sencha artifacts</name> | |
<url>https://maven.sencha.com/repo/gpl-release/</url> | |
</repository> | |
</repositories> | |
<dependencies> | |
<!-- Google Web Toolkit (GWT) --> | |
<dependency> | |
<groupId>com.google.gwt</groupId> | |
<artifactId>gwt-user</artifactId> | |
<version>${gwtVersion}</version> | |
<scope>provided</scope> | |
</dependency> | |
<!-- For the servlet filter --> | |
<dependency> | |
<groupId>javax.servlet</groupId> | |
<artifactId>servlet-api</artifactId> | |
<version>2.5</version> | |
<scope>provided</scope> | |
</dependency> | |
<!-- RequestFactory server --> | |
<dependency> | |
<groupId>com.google.web.bindery</groupId> | |
<artifactId>requestfactory-server</artifactId> | |
<version>2.4.0</version> | |
</dependency> | |
<!-- RequestFactory will use JSR 303 javax.validation if you let it --> | |
<dependency> | |
<groupId>javax.validation</groupId> | |
<artifactId>validation-api</artifactId> | |
<version>1.0.0.GA</version> | |
<classifier>sources</classifier> | |
</dependency> | |
<dependency> | |
<groupId>org.hibernate</groupId> | |
<artifactId>hibernate-validator</artifactId> | |
<version>4.2.0.Final</version> | |
<exclusions> | |
<exclusion> | |
<groupId>javax.xml.bind</groupId> | |
<artifactId>jaxb-api</artifactId> | |
</exclusion> | |
<exclusion> | |
<groupId>com.sun.xml.bind</groupId> | |
<artifactId>jaxb-impl</artifactId> | |
</exclusion> | |
</exclusions> | |
</dependency> | |
<!-- Required by Hibernate validator because slf4j-log4j is | |
optional in the hibernate-validator POM --> | |
<dependency> | |
<groupId>org.slf4j</groupId> | |
<artifactId>slf4j-log4j12</artifactId> | |
<version>1.6.1</version> | |
</dependency> | |
<dependency> | |
<groupId>org.slf4j</groupId> | |
<artifactId>slf4j-api</artifactId> | |
<version>1.6.1</version> | |
</dependency> | |
<!-- sencha gxt3 --> | |
<dependency> | |
<groupId>com.sencha.gxt</groupId> | |
<artifactId>gxt</artifactId> | |
<version>3.0.0b</version> | |
</dependency> | |
<!-- sencha uibinder support --> | |
<dependency> | |
<groupId>com.sencha.gxt</groupId> | |
<artifactId>uibinder-bridge</artifactId> | |
<version>2.4.0</version> | |
</dependency> | |
<!-- google guice --> | |
<dependency> | |
<groupId>com.google.inject</groupId> | |
<artifactId>guice</artifactId> | |
<version>3.0</version> | |
</dependency> | |
<!-- google guice-servlet --> | |
<dependency> | |
<groupId>com.google.inject.extensions</groupId> | |
<artifactId>guice-servlet</artifactId> | |
<version>3.0</version> | |
</dependency> | |
<!-- google guice-persist --> | |
<dependency> | |
<groupId>com.google.inject.extensions</groupId> | |
<artifactId>guice-persist</artifactId> | |
<version>3.0</version> | |
</dependency> | |
<!-- google-gin --> | |
<dependency> | |
<groupId>com.google.gwt.inject</groupId> | |
<artifactId>gin</artifactId> | |
<version>1.5.0</version> | |
<exclusions> | |
<exclusion> | |
<groupId>com.google.gwt</groupId> | |
<artifactId>gwt-servlet</artifactId> | |
</exclusion> | |
</exclusions> | |
</dependency> | |
<!-- hibernate core --> | |
<dependency> | |
<groupId>org.hibernate</groupId> | |
<artifactId>hibernate-core</artifactId> | |
<version>3.6.10.Final</version> | |
</dependency> | |
<!-- hibernate jpa --> | |
<dependency> | |
<groupId>org.hibernate</groupId> | |
<artifactId>hibernate-entitymanager</artifactId> | |
<version>3.6.10.Final</version> | |
</dependency> | |
<!-- hibernate metamodel --> | |
<dependency> | |
<groupId>org.hibernate</groupId> | |
<artifactId>hibernate-jpamodelgen</artifactId> | |
<version>1.2.0.Final</version> | |
</dependency> | |
<!-- postgresql jdbc connector --> | |
<dependency> | |
<groupId>postgresql</groupId> | |
<artifactId>postgresql</artifactId> | |
<version>9.1-901-1.jdbc4</version> | |
</dependency> | |
</dependencies> | |
<build> | |
<!-- Generate compiled stuff in the folder used for developing mode --> | |
<outputDirectory>${webappDirectory}/WEB-INF/classes</outputDirectory> | |
<plugins> | |
<!-- requestfactory-apt runs an annotation processor (APT) to | |
instrument its service interfaces so that | |
RequestFactoryServer can decode client requests. Normally | |
you would just have a dependency on requestfactory-apt | |
with <scope>provided</scope>, but that won't work in | |
eclipse due to m2e bug | |
https://bugs.eclipse.org/bugs/show_bug.cgi?id=335036 --> | |
<plugin> | |
<groupId>org.bsc.maven</groupId> | |
<artifactId>maven-processor-plugin</artifactId> | |
<version>2.0.5</version> | |
<executions> | |
<execution> | |
<id>process</id> | |
<goals> | |
<goal>process</goal> | |
</goals> | |
<phase>generate-sources</phase> | |
<configuration> | |
<outputDirectory>${project.build.directory}/generated-sources/gwt</outputDirectory> | |
</configuration> | |
</execution> | |
</executions> | |
<dependencies> | |
<dependency> | |
<groupId>com.google.web.bindery</groupId> | |
<artifactId>requestfactory-apt</artifactId> | |
<version>${gwtVersion}</version> | |
</dependency> | |
</dependencies> | |
</plugin> | |
<!-- Google Plugin for Eclipse (GPE) won't see the source | |
generated above by requestfactory-apt unless it is exposed | |
as an additional source dir--> | |
<plugin> | |
<groupId>org.codehaus.mojo</groupId> | |
<artifactId>build-helper-maven-plugin</artifactId> | |
<version>1.7</version> | |
<executions> | |
<execution> | |
<id>add-source</id> | |
<phase>generate-sources</phase> | |
<goals> | |
<goal>add-source</goal> | |
</goals> | |
<configuration> | |
<sources> | |
<source>${project.build.directory}/generated-sources/gwt</source> | |
</sources> | |
</configuration> | |
</execution> | |
</executions> | |
</plugin> | |
<!-- GWT Maven Plugin --> | |
<plugin> | |
<groupId>org.codehaus.mojo</groupId> | |
<artifactId>gwt-maven-plugin</artifactId> | |
<version>2.4.0</version> | |
<executions> | |
<execution> | |
<goals> | |
<goal>compile</goal> | |
<goal>test</goal> | |
<goal>i18n</goal> | |
<goal>generateAsync</goal> | |
</goals> | |
</execution> | |
</executions> | |
<!-- Plugin configuration. There are many available options, see | |
gwt-maven-plugin documentation at codehaus.org --> | |
<configuration> | |
<runTarget>example.html</runTarget> | |
<hostedWebapp>${webappDirectory}</hostedWebapp> | |
<i18nMessagesBundle>com.example.client.Messages</i18nMessagesBundle> | |
</configuration> | |
</plugin> | |
<!-- Copy static web files before executing gwt:run --> | |
<plugin> | |
<groupId>org.apache.maven.plugins</groupId> | |
<artifactId>maven-war-plugin</artifactId> | |
<version>2.1.1</version> | |
<executions> | |
<execution> | |
<phase>compile</phase> | |
<goals> | |
<goal>exploded</goal> | |
</goals> | |
</execution> | |
</executions> | |
<configuration> | |
<webappDirectory>${webappDirectory}</webappDirectory> | |
</configuration> | |
</plugin> | |
<plugin> | |
<groupId>org.apache.maven.plugins</groupId> | |
<artifactId>maven-compiler-plugin</artifactId> | |
<version>2.3.2</version> | |
<configuration> | |
<source>1.5</source> | |
<target>1.5</target> | |
</configuration> | |
</plugin> | |
</plugins> | |
<pluginManagement> | |
<plugins> | |
<!--This plugin's configuration is used to store Eclipse m2e settings only. It has no influence on the Maven build itself.--> | |
<plugin> | |
<groupId>org.eclipse.m2e</groupId> | |
<artifactId>lifecycle-mapping</artifactId> | |
<version>1.0.0</version> | |
<configuration> | |
<lifecycleMappingMetadata> | |
<pluginExecutions> | |
<pluginExecution> | |
<pluginExecutionFilter> | |
<groupId>org.codehaus.mojo</groupId> | |
<artifactId> | |
gwt-maven-plugin | |
</artifactId> | |
<versionRange> | |
[2.4.0,) | |
</versionRange> | |
<goals> | |
<goal>generateAsync</goal> | |
<goal>i18n</goal> | |
</goals> | |
</pluginExecutionFilter> | |
<action> | |
<ignore></ignore> | |
</action> | |
</pluginExecution> | |
<pluginExecution> | |
<pluginExecutionFilter> | |
<groupId>org.bsc.maven</groupId> | |
<artifactId> | |
maven-processor-plugin | |
</artifactId> | |
<versionRange> | |
[2.0.5,) | |
</versionRange> | |
<goals> | |
<goal>process</goal> | |
</goals> | |
</pluginExecutionFilter> | |
<action> | |
<ignore></ignore> | |
</action> | |
</pluginExecution> | |
</pluginExecutions> | |
</lifecycleMappingMetadata> | |
</configuration> | |
</plugin> | |
</plugins> | |
</pluginManagement> | |
</build> | |
</project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="UTF-8"?> | |
<module rename-to='example'> | |
<inherits name='com.google.gwt.user.User' /> | |
<inherits name='com.google.gwt.i18n.I18N'/> | |
<inherits name='com.google.web.bindery.requestfactory.RequestFactory' /> | |
<inherits name='com.sencha.gxt.ui.GXT'/> | |
<inherits name="com.sencha.gwt.uibinder.UiBinder" /> | |
<inherits name="com.google.gwt.inject.Inject"/> | |
<entry-point class='com.example.client.example' /> | |
<extend-property name="locale" values="es_PE"/> | |
<!-- Specify the paths for translatable code --> | |
<source path='client' /> | |
<source path='shared' /> | |
</module> |
EntryPoint
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.example.client; | |
import com.example.client.gin.ClientGinjector; | |
import com.google.gwt.core.client.EntryPoint; | |
import com.google.gwt.core.client.GWT; | |
import com.google.gwt.user.client.ui.RootPanel; | |
public class example implements EntryPoint { | |
private final ClientGinjector injector = GWT.create(ClientGinjector.class); | |
public void onModuleLoad() { | |
RootPanel.get().add(injector.getColaboradorPanel()); | |
} | |
} |
What is Dependency Intection?
In a nutshell "Never use new use @Inject instead". Guice will create your object and initialized injected fields.
In GIN I need a Ginjector and create this with GWT.create().
private final ClientGinjector injector = GWT.create(ClientGinjector.class);
Ginjector code
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.example.client.gin; | |
import com.example.client.components.ColaboradorPanel; | |
import com.google.gwt.inject.client.GinModules; | |
import com.google.gwt.inject.client.Ginjector; | |
@GinModules(ClientModule.class) | |
public interface ClientGinjector extends Ginjector { | |
ColaboradorPanel getColaboradorPanel(); | |
} |
Client Module
The next code was extracted from http://cleancodematters.com/2011/05/19/gwt-requestfactory_with_gin/
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.example.client.gin; | |
import com.example.client.Messages; | |
import com.example.client.images.AppImages; | |
import com.example.shared.service.AppRequestFactory; | |
import com.google.gwt.core.client.GWT; | |
import com.google.gwt.inject.client.AbstractGinModule; | |
import com.google.inject.Provides; | |
import com.google.inject.Singleton; | |
import com.google.web.bindery.event.shared.EventBus; | |
import com.google.web.bindery.event.shared.SimpleEventBus; | |
public class ClientModule extends AbstractGinModule { | |
@Override | |
protected void configure() { | |
bind(AppImages.class).in(Singleton.class); | |
bind(Messages.class).in(Singleton.class); | |
bind(EventBus.class).to(SimpleEventBus.class).in(Singleton.class); | |
} | |
@Provides | |
@Singleton | |
public AppRequestFactory createRequestFactory(EventBus eventBus) { | |
AppRequestFactory factory = GWT.create(AppRequestFactory.class); | |
factory.initialize(eventBus); | |
return factory; | |
} | |
} |
ColaboradorPanel
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.example.client.components; | |
import java.util.ArrayList; | |
import java.util.List; | |
import com.example.client.Messages; | |
import com.example.client.events.SaveEvent; | |
import com.example.client.events.SaveEvent.SaveHandler; | |
import com.example.shared.proxy.ColaboradorProxy; | |
import com.example.shared.service.AppRequestFactory; | |
import com.example.shared.service.ColaboradorService; | |
import com.google.gwt.core.client.GWT; | |
import com.google.gwt.core.client.Scheduler; | |
import com.google.gwt.core.client.Scheduler.ScheduledCommand; | |
import com.google.gwt.uibinder.client.UiBinder; | |
import com.google.gwt.uibinder.client.UiField; | |
import com.google.gwt.uibinder.client.UiHandler; | |
import com.google.gwt.user.client.ui.IsWidget; | |
import com.google.gwt.user.client.ui.Widget; | |
import com.google.inject.Inject; | |
import com.google.inject.Provider; | |
import com.google.web.bindery.event.shared.EventBus; | |
import com.google.web.bindery.requestfactory.gwt.client.RequestFactoryEditorDriver; | |
import com.google.web.bindery.requestfactory.shared.Receiver; | |
import com.google.web.bindery.requestfactory.shared.RequestContext; | |
import com.sencha.gxt.core.client.IdentityValueProvider; | |
import com.sencha.gxt.core.client.Style.SelectionMode; | |
import com.sencha.gxt.core.client.ValueProvider; | |
import com.sencha.gxt.data.shared.ListStore; | |
import com.sencha.gxt.data.shared.ModelKeyProvider; | |
import com.sencha.gxt.data.shared.PropertyAccess; | |
import com.sencha.gxt.data.shared.SortInfo; | |
import com.sencha.gxt.data.shared.loader.FilterConfig; | |
import com.sencha.gxt.data.shared.loader.FilterPagingLoadConfig; | |
import com.sencha.gxt.data.shared.loader.FilterPagingLoadConfigBean; | |
import com.sencha.gxt.data.shared.loader.LoadResultListStoreBinding; | |
import com.sencha.gxt.data.shared.loader.PagingLoadResult; | |
import com.sencha.gxt.data.shared.loader.PagingLoader; | |
import com.sencha.gxt.data.shared.loader.RequestFactoryProxy; | |
import com.sencha.gxt.widget.core.client.Dialog; | |
import com.sencha.gxt.widget.core.client.box.ConfirmMessageBox; | |
import com.sencha.gxt.widget.core.client.button.TextButton; | |
import com.sencha.gxt.widget.core.client.event.HideEvent; | |
import com.sencha.gxt.widget.core.client.event.HideEvent.HideHandler; | |
import com.sencha.gxt.widget.core.client.event.SelectEvent; | |
import com.sencha.gxt.widget.core.client.form.NumberPropertyEditor; | |
import com.sencha.gxt.widget.core.client.grid.CheckBoxSelectionModel; | |
import com.sencha.gxt.widget.core.client.grid.ColumnConfig; | |
import com.sencha.gxt.widget.core.client.grid.ColumnModel; | |
import com.sencha.gxt.widget.core.client.grid.Grid; | |
import com.sencha.gxt.widget.core.client.grid.filters.GridFilters; | |
import com.sencha.gxt.widget.core.client.grid.filters.NumericFilter; | |
import com.sencha.gxt.widget.core.client.grid.filters.StringFilter; | |
import com.sencha.gxt.widget.core.client.info.Info; | |
import com.sencha.gxt.widget.core.client.selection.SelectionChangedEvent; | |
import com.sencha.gxt.widget.core.client.selection.SelectionChangedEvent.SelectionChangedHandler; | |
import com.sencha.gxt.widget.core.client.toolbar.PagingToolBar; | |
public class ColaboradorPanel implements IsWidget { | |
interface ColaboradorProxyProperties extends PropertyAccess<ColaboradorProxy> { | |
ModelKeyProvider<ColaboradorProxy> id(); | |
ValueProvider<ColaboradorProxy, String> nombres(); | |
ValueProvider<ColaboradorProxy, String> apellidos(); | |
ValueProvider<ColaboradorProxy, Integer> edad(); | |
} | |
public interface Binder extends UiBinder<Widget, ColaboradorPanel> { | |
} | |
public interface Driver extends RequestFactoryEditorDriver<ColaboradorProxy, ColaboradorEditor>{ | |
} | |
private final SaveHandler saveHandler = new SaveHandler() { | |
public void onSave(SaveEvent event) { | |
RequestContext context = driver.flush(); | |
if(!driver.hasErrors()){ | |
editor.hide(); | |
context.fire(); | |
} | |
} | |
}; | |
@Inject Messages messages; | |
private ColaboradorProxy colaborador; | |
private ListStore<ColaboradorProxy> store; | |
private PagingLoader<FilterPagingLoadConfig, PagingLoadResult<ColaboradorProxy>> loader; | |
private final Driver driver; | |
private final ColaboradorEditor editor; | |
private final AppRequestFactory factory; | |
private final Binder uiBinder; | |
@UiField(provided = true) | |
Grid<ColaboradorProxy> grid; | |
@UiField(provided = true) | |
PagingToolBar toolBar; | |
@UiField | |
TextButton edit; | |
@UiField | |
TextButton delete; | |
@Inject | |
public ColaboradorPanel(final Binder uiBinder, EventBus eventBus, Provider<AppRequestFactory> provider, Driver driver, ColaboradorEditor editor){ | |
this.uiBinder = uiBinder; | |
this.editor = editor; | |
this.driver = driver; | |
this.factory = provider.get(); | |
driver.initialize(factory, editor); | |
eventBus.addHandler(SaveEvent.getType(), saveHandler); | |
} | |
public Widget asWidget() { | |
RequestFactoryProxy<FilterPagingLoadConfig, PagingLoadResult<ColaboradorProxy>> proxy = new RequestFactoryProxy<FilterPagingLoadConfig, PagingLoadResult<ColaboradorProxy>>() { | |
@Override | |
public void load(FilterPagingLoadConfig loadConfig, | |
Receiver<? super PagingLoadResult<ColaboradorProxy>> receiver) { | |
ColaboradorService cs = factory.colaboradorService(); | |
List<SortInfo> sortInfo = createRequestSortInfo(cs, loadConfig.getSortInfo()); | |
List<FilterConfig> filterConfig = createRequestFilterConfig(cs, loadConfig.getFilters()); | |
cs.list(loadConfig.getOffset(), loadConfig.getLimit(), sortInfo, filterConfig).to(receiver); | |
cs.fire(); | |
} | |
}; | |
loader = new PagingLoader<FilterPagingLoadConfig, PagingLoadResult<ColaboradorProxy>>(proxy){ | |
@Override | |
protected FilterPagingLoadConfig newLoadConfig() { | |
return new FilterPagingLoadConfigBean(); | |
} | |
}; | |
loader.setRemoteSort(true); | |
ColaboradorProxyProperties props = GWT.create(ColaboradorProxyProperties.class); | |
store = new ListStore<ColaboradorProxy>(props.id()); | |
loader.addLoadHandler(new LoadResultListStoreBinding<FilterPagingLoadConfig, ColaboradorProxy, PagingLoadResult<ColaboradorProxy>>(store)); | |
toolBar = new PagingToolBar(2); | |
toolBar.bind(loader); | |
toolBar.getElement().getStyle().setProperty("borderBottom", "none"); | |
IdentityValueProvider<ColaboradorProxy> identity = new IdentityValueProvider<ColaboradorProxy>(); | |
final CheckBoxSelectionModel<ColaboradorProxy> sm = new CheckBoxSelectionModel<ColaboradorProxy>(identity); | |
sm.setSelectionMode(SelectionMode.MULTI); | |
ColumnConfig<ColaboradorProxy, String> nombresColumn = new ColumnConfig<ColaboradorProxy, String>(props.nombres(), 150, messages.firstName()); | |
ColumnConfig<ColaboradorProxy, String> apellidosColumn = new ColumnConfig<ColaboradorProxy, String>(props.apellidos(), 150, messages.lastName()); | |
ColumnConfig<ColaboradorProxy, Integer> edadColumn = new ColumnConfig<ColaboradorProxy, Integer>(props.edad(), 80, messages.age()); | |
List<ColumnConfig<ColaboradorProxy, ?>> l = new ArrayList<ColumnConfig<ColaboradorProxy, ?>>(); | |
l.add(sm.getColumn()); | |
l.add(nombresColumn); | |
l.add(apellidosColumn); | |
l.add(edadColumn); | |
ColumnModel<ColaboradorProxy> cm = new ColumnModel<ColaboradorProxy>(l); | |
grid = new Grid<ColaboradorProxy>(store, cm) { | |
@Override | |
protected void onAfterFirstAttach() { | |
super.onAfterFirstAttach(); | |
Scheduler.get().scheduleDeferred(new ScheduledCommand() { | |
public void execute() { | |
loader.load(); | |
} | |
}); | |
} | |
}; | |
grid.setLoader(loader); | |
grid.setSelectionModel(sm); | |
grid.getView().setStripeRows(true); | |
GridFilters<ColaboradorProxy> filters = new GridFilters<ColaboradorProxy>(loader); | |
filters.initPlugin(grid); | |
filters.setLocal(false); | |
filters.addFilter(new StringFilter<ColaboradorProxy>(props.nombres())); | |
filters.addFilter(new StringFilter<ColaboradorProxy>(props.apellidos())); | |
filters.addFilter(new NumericFilter<ColaboradorProxy, Integer>(props.edad(), new NumberPropertyEditor.IntegerPropertyEditor())); | |
grid.getSelectionModel().addSelectionChangedHandler(new SelectionChangedHandler<ColaboradorProxy>() { | |
public void onSelectionChanged( | |
SelectionChangedEvent<ColaboradorProxy> event) { | |
int size = event.getSelection().size(); | |
if(size == 0){ | |
edit.setEnabled(false); | |
delete.setEnabled(false); | |
}else if(size == 1){ | |
edit.setEnabled(true); | |
delete.setEnabled(true); | |
}else if(size > 1){ | |
edit.setEnabled(false); | |
delete.setEnabled(true); | |
} | |
} | |
}); | |
return uiBinder.createAndBindUi(this); | |
} | |
@UiHandler("add") | |
public void onAdd(SelectEvent event){ | |
ColaboradorService cs = factory.colaboradorService(); | |
colaborador = cs.create(ColaboradorProxy.class); | |
cs.persist(colaborador).to(new Receiver<ColaboradorProxy>() { | |
@Override | |
public void onSuccess(ColaboradorProxy response) { | |
Info.display(messages.titlePanel(), messages.saveSuccessful()); | |
loader.load(); | |
} | |
}); | |
driver.edit(colaborador, cs); | |
editor.clearFields(); | |
editor.show(messages.addColaborador()); | |
} | |
@UiHandler("edit") | |
public void onEdit(SelectEvent event){ | |
ColaboradorService cs = factory.colaboradorService(); | |
colaborador = grid.getSelectionModel().getSelectedItem(); | |
cs.persist(colaborador); | |
driver.edit(colaborador, cs); | |
editor.clearFields(); | |
editor.show(messages.editColaborador()); | |
} | |
@UiHandler("delete") | |
public void onDelete(SelectEvent event){ | |
List<ColaboradorProxy> colaboradores = grid.getSelectionModel().getSelectedItems(); | |
String mensaje; | |
if(colaboradores.size() == 1) | |
mensaje = messages.deleteConfirm(colaboradores.get(0).getNombres()); | |
else | |
mensaje = messages.deleteAllConfirm(); | |
ConfirmMessageBox box = new ConfirmMessageBox(messages.titlePanel(), mensaje); | |
box.addHideHandler(new HideHandler() { | |
public void onHide(HideEvent event) { | |
Dialog btn = (Dialog) event.getSource(); | |
if(!"No".equals(btn.getHideButton().getText())){ | |
ColaboradorService cs = factory.colaboradorService(); | |
List<ColaboradorProxy> colaboradores = grid.getSelectionModel().getSelectedItems(); | |
cs.remove(colaboradores).fire(new Receiver<Void>() { | |
@Override | |
public void onSuccess(Void response) { | |
Info.display(messages.titlePanel(), messages.deleteSuccessful()); | |
loader.load(); | |
} | |
}); | |
} | |
} | |
}); | |
box.show(); | |
} | |
} |
I created a SaveEvent to be fired when a user click on Save Button.
SaveEvent
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.example.client.events; | |
import com.google.gwt.event.shared.GwtEvent; | |
import com.google.gwt.event.shared.EventHandler; | |
import com.google.gwt.event.shared.HasHandlers; | |
public class SaveEvent extends GwtEvent<SaveEvent.SaveHandler> { | |
public static Type<SaveHandler> TYPE = new Type<SaveHandler>(); | |
public interface SaveHandler extends EventHandler { | |
void onSave(SaveEvent event); | |
} | |
public SaveEvent() { | |
} | |
@Override | |
protected void dispatch(SaveHandler handler) { | |
handler.onSave(this); | |
} | |
@Override | |
public Type<SaveHandler> getAssociatedType() { | |
return TYPE; | |
} | |
public static Type<SaveHandler> getType() { | |
return TYPE; | |
} | |
public static void fire(HasHandlers source) { | |
source.fireEvent(new SaveEvent()); | |
} | |
} |
I created this class using a GWTP Plugin for Eclipse http://code.google.com/p/gwt-platform/wiki/EclipsePlugin Once installed you can use this wizard to easily create your events.
I entered the EventName: SaveEvent and check the Has Handlers box.
The Editor is the Dialog where the user entered your information and click on Save button. Here the SaveEvent is fired and the method save of SaveHandler is executed. Here I call the flush method to update the object modified in the editor. An next if the driver has not eny errors I hide the Editor and fire the context RequestContext.
What Is a RequestContext
In RequestFactory all changes in an object are done inside a context. When you want to persist your changes call the fire methot of a context. Your changes are sent to the server and are saved to a database. It means you can make many changes before sent your changes to the server.
Next I Inject Messages interface using Field Injection.
@Inject Messages messages;
Then I use Constructor Injection to initialized required variables.
@Inject
public ColaboradorPanel(final Binder uiBinder, EventBus eventBus, Provider<AppRequestFactory> provider, Driver driver, ColaboradorEditor editor)
What happens first Constructor Injection or Field Injection?
The answer is Constructor Injection. It means you can't use messages variable inside the constructor because you will get a NullPointerException. If you need a variable inside a constructor inject it using Constructor Injection.
For create a GXT Grid you need create a proxy, loader and a store. Inside the proxy I use two helper function createRequestSortInfo createRequestFilterConfig to get sort info and filter info. This is needed because in Request Factory you cannot sent any object to the server. You only can sent interfaces that extends ValueProxy or EntityProxy. You sent a EntityProxy if there is an Entity Implementation on the server It means if you have a table in a database. For other objects use ValueProxy.
For create objects and edit In RequestFactory with GWT Editor. I use one method persist()
Create an Object with GWT Editor
First I declare a field member
private ColaboradorProxy colaborador;
If I wanna create an object I create en empty Object
colaborador = cs.create(ColaboradorProxy.class);
Then bind the object with the Editor
driver.edit(colaborador, cs);
Next show the Editor
editor.show(messages.addColaborador());
When the user see the Editor or Dialog will see empty TextFields because is binded with an empty object.
Then the user fill the Form and click on Save. The SaveEvent is fired and The SaveHandler is executed. Here I still have an empty object I you do a System.out.println(colaborador.getAge()) you will get nothing then I call the method flush() on driver and the empty object is update with the editor information the user was entered next you can do System.out.println(colaborador.getAge()) an will get what you want.
Next I fire the context and sent my changes to server.
Edit an Object with GWT Editor
Get the object selected
colaborador = grid.getSelectionModel().getSelectedItem();
And repeat the above steps.
When the user see the editor will see the information selected and can do changes and save them because It is binded with an selected object and is not empty. Here the same happens if you don't call the method flush() on driver your object won't be updated.
Two way for creating UiBinder
When you use UiBinder you should have two files ColaboradorPanel.java and ColaboradorPanel.ui.xml
Your java file may be writed in two ways:
First way
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class ColaboradorPanel extends Composite { | |
public interface Binder extends UiBinder<Widget, ColaboradorPanel> { | |
} | |
private static Binder uiBinder = GWT.create(Binder.class); | |
public ColaboradorPanel() { | |
initWidget(uiBinder.createAndBindUi(this)); | |
} | |
} |
If you want to extend from Window you can see http://www.sencha.com/forum/showthread.php?181348-(beta2)-Editor-extends-Window-doesn-t-work
Second way
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class ColaboradorPanel implements IsWidget { | |
public interface Binder extends UiBinder<Widget, ColaboradorPanel> { | |
} | |
private static Binder uiBinder = GWT.create(Binder.class); | |
public ColaboradorPanel(){ | |
} | |
public Widget asWidget() { | |
return uiBinder.createAndBindUi(this); | |
} | |
} |
ColaboradorPanel.ui.xml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent"> | |
<ui:UiBinder xmlns:ui="urn:ui:com.google.gwt.uibinder" | |
xmlns:g="urn:import:com.google.gwt.user.client.ui" | |
xmlns:gxt="urn:import:com.sencha.gxt.widget.core.client" | |
xmlns:container="urn:import:com.sencha.gxt.widget.core.client.container" | |
xmlns:toolbar="urn:import:com.sencha.gxt.widget.core.client.toolbar" | |
xmlns:button="urn:import:com.sencha.gxt.widget.core.client.button" | |
xmlns:grid="urn:import:com.sencha.gxt.widget.core.client.grid" > | |
<ui:with type="com.example.client.images.AppImages" field="images" /> | |
<ui:with type="com.example.client.Messages" field="messages" /> | |
<ui:with type="com.sencha.gxt.widget.core.client.container.VerticalLayoutContainer.VerticalLayoutData" field="verticalLayoutData"> | |
<ui:attributes width="1" height="-1" /> | |
</ui:with> | |
<ui:with type="com.sencha.gxt.widget.core.client.container.VerticalLayoutContainer.VerticalLayoutData" field="centerLayoutData"> | |
<ui:attributes width="1" height="1" /> | |
</ui:with> | |
<ui:style> | |
.background { | |
background-color: white; | |
} | |
</ui:style> | |
<g:VerticalPanel spacing="10"> | |
<gxt:FramedPanel ui:field="panel" collapsible="true" headingText="{messages.titlePanel}" pixelSize="550, 300"> | |
<container:VerticalLayoutContainer borders="true" addStyleNames="{style.background}"> | |
<container:child layoutData="{verticalLayoutData}"> | |
<toolbar:ToolBar> | |
<button:TextButton text="{messages.add}" ui:field="add" icon="{images.add}" /> | |
<toolbar:SeparatorToolItem /> | |
<button:TextButton text="{messages.edit}" ui:field="edit" icon="{images.update}" enabled="false" /> | |
<toolbar:SeparatorToolItem /> | |
<button:TextButton text="{messages.delete}" ui:field="delete" icon="{images.delete}" enabled="false" /> | |
<toolbar:FillToolItem /> | |
</toolbar:ToolBar> | |
</container:child> | |
<container:child layoutData="{centerLayoutData}" > | |
<grid:Grid ui:field="grid" /> | |
</container:child> | |
<container:child layoutData="{verticalLayoutData}"> | |
<toolbar:PagingToolBar ui:field="toolBar" /> | |
</container:child> | |
</container:VerticalLayoutContainer> | |
</gxt:FramedPanel> | |
</g:VerticalPanel> | |
</ui:UiBinder> |
ColaboradorEditor.java
All fields that need binding should be declared like class fields. If the widget is only in UiBinder and not declared like class field the binding with GWT Editor does not work.
In UiBinder all widgets are create by GWT. If you need pass an argument by UiBinder you can use
@UiFactory http://blog.jeffdouglas.com/2010/02/24/gwt-uibinder-passing-objects-to-widgets/
In another way you can pass the argument in java code and your field should be annotated with @UiField(provided=true) what means the object will be created by java code and not by UiBinder.
For add events I use @UiHandler("save") annotation where "save" identifies the button Save.
ColaboradorEditor.ui.xml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent"> | |
<ui:UiBinder xmlns:ui="urn:ui:com.google.gwt.uibinder" | |
xmlns:g="urn:import:com.google.gwt.user.client.ui" | |
xmlns:gxt="urn:import:com.sencha.gxt.widget.core.client" | |
xmlns:form="urn:import:com.sencha.gxt.widget.core.client.form" | |
xmlns:container="urn:import:com.sencha.gxt.widget.core.client.container" | |
xmlns:button="urn:import:com.sencha.gxt.widget.core.client.button" > | |
<ui:with type="com.example.client.images.AppImages" field="images" /> | |
<ui:with type="com.example.client.Messages" field="messages" /> | |
<ui:with type="com.sencha.gxt.widget.core.client.container.VerticalLayoutContainer.VerticalLayoutData" field="verticalLayoutData"> | |
<ui:attributes width="1" height="-1" /> | |
</ui:with> | |
<ui:style> | |
.important { | |
font-weight: bold; | |
} | |
</ui:style> | |
<gxt:Window buttonAlign="END" resizable="false" > | |
<gxt:FramedPanel ui:field="form" headerVisible="false" > | |
<form:FieldSet ui:field="fieldSet" headingText="{messages.infoUser}" collapsible="false" > | |
<container:VerticalLayoutContainer> | |
<container:child layoutData="{verticalLayoutData}"> | |
<form:FieldLabel text="{messages.firstName}"> | |
<form:widget> | |
<form:TextField ui:field="nombres" allowBlank="false" /> | |
</form:widget> | |
</form:FieldLabel> | |
</container:child> | |
<container:child layoutData="{verticalLayoutData}"> | |
<form:FieldLabel text="{messages.lastName}"> | |
<form:widget> | |
<form:TextField ui:field="apellidos" allowBlank="false" /> | |
</form:widget> | |
</form:FieldLabel> | |
</container:child> | |
<container:child layoutData="{verticalLayoutData}"> | |
<form:FieldLabel text="{messages.age}"> | |
<form:widget> | |
<form:NumberField ui:field="edad" allowDecimals="false" allowNegative="false" /> | |
</form:widget> | |
</form:FieldLabel> | |
</container:child> | |
</container:VerticalLayoutContainer> | |
</form:FieldSet> | |
</gxt:FramedPanel> | |
<gxt:button> | |
<button:TextButton text="{messages.cancel}" ui:field="cancel" icon="{images.cancel}" /> | |
</gxt:button> | |
<gxt:button> | |
<button:TextButton text="{messages.save}" ui:field="save" icon="{images.save}" /> | |
</gxt:button> | |
</gxt:Window> | |
</ui:UiBinder> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.example.client.images; | |
import com.google.gwt.resources.client.ClientBundle; | |
import com.google.gwt.resources.client.ImageResource; | |
public interface AppImages extends ClientBundle { | |
@Source("add.gif") | |
ImageResource add(); | |
@Source("delete.gif") | |
ImageResource delete(); | |
@Source("update.png") | |
ImageResource update(); | |
@Source("save.png") | |
ImageResource save(); | |
@Source("cancel.png") | |
ImageResource cancel(); | |
} |
EntityBase.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.example.server.domain; | |
import java.io.Serializable; | |
import javax.persistence.GeneratedValue; | |
import javax.persistence.GenerationType; | |
import javax.persistence.Id; | |
import javax.persistence.MappedSuperclass; | |
import javax.persistence.PrePersist; | |
/** | |
* EntityBase, base entity for all entities. | |
*/ | |
@MappedSuperclass | |
public abstract class EntityBase implements Serializable { | |
private static final long serialVersionUID = 1L; | |
@Id | |
@GeneratedValue(strategy=GenerationType.IDENTITY) | |
protected Long id; | |
private Integer version = 0; | |
/** | |
* Auto-increment version # whenever persisted | |
*/ | |
@PrePersist | |
void onPersist() | |
{ | |
this.version++; | |
} | |
public Long getId() { | |
return id; | |
} | |
public Integer getVersion() { | |
return version; | |
} | |
} |
Colaborador.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.example.server.domain; | |
import javax.persistence.Entity; | |
import javax.persistence.Table; | |
/** | |
* The persistent class for the colaboradores database table. | |
* | |
*/ | |
@Entity | |
@Table(name="colaboradores") | |
public class Colaborador extends EntityBase { | |
private static final long serialVersionUID = 1L; | |
private String apellidos; | |
private Integer edad; | |
private String nombres; | |
public Colaborador() { | |
} | |
public String getApellidos() { | |
return this.apellidos; | |
} | |
public void setApellidos(String apellidos) { | |
this.apellidos = apellidos; | |
} | |
public Integer getEdad() { | |
return this.edad; | |
} | |
public void setEdad(Integer edad) { | |
this.edad = edad; | |
} | |
public String getNombres() { | |
return this.nombres; | |
} | |
public void setNombres(String nombres) { | |
this.nombres = nombres; | |
} | |
} |
First you should enable JPA Facet in your eclipse project.
You need configure a data source Window -->Show View --> Data Source Explorer
and create a new connection
Right click on your package and select New --> JPA Entities from tables
RequestFactory with Guice
RequestFactory is not integrate with Guice por default. In that case there is a helpful set of classes that make RequestFactory injectable https://github.com/etiennep/injected-requestfactory. I only did copy and paste of that classes.
web.xml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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> | |
<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>com.example.server.MyGuiceServletConfig</listener-class> | |
</listener> | |
<!-- Default page to serve --> | |
<welcome-file-list> | |
<welcome-file>example.html</welcome-file> | |
</welcome-file-list> | |
</web-app> |
MyGuiceServletConfig.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.example.server; | |
import com.example.server.requestfactory.InjectedRequestFactoryModule; | |
import com.example.server.requestfactory.InjectedRequestFactoryServlet; | |
import com.google.inject.Guice; | |
import com.google.inject.Injector; | |
import com.google.inject.persist.PersistFilter; | |
import com.google.inject.persist.jpa.JpaPersistModule; | |
import com.google.inject.servlet.GuiceServletContextListener; | |
import com.google.inject.servlet.ServletModule; | |
public class MyGuiceServletConfig extends GuiceServletContextListener { | |
@Override | |
protected Injector getInjector() { | |
return Guice.createInjector( | |
new ServletModule(){ | |
@Override | |
protected void configureServlets() { | |
install(new JpaPersistModule("myFirstJpaUnit")); // like we saw earlier. | |
filter("/*").through(PersistFilter.class); | |
install(new InjectedRequestFactoryModule()); | |
serve("/gwtRequest").with(InjectedRequestFactoryServlet.class); | |
//filter("/*").through(MyFilter.class); | |
//filter("*.css").through(MyCssFilter.class); | |
// etc.. | |
//serve("*.html").with(MyServlet.class); | |
//serve("/my/*").with(MyServlet.class); | |
// etc.. | |
} | |
}); | |
} | |
} |
In Guice you can configure all servlets and filters in java code here I configure my InjectedRequestFactoryServlet not use web.xml anymore. More info http://code.google.com/p/google-guice/wiki/ServletModule.
persistence.xml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="UTF-8"?> | |
<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"> | |
<persistence-unit name="myFirstJpaUnit" transaction-type="RESOURCE_LOCAL" > | |
<provider>org.hibernate.ejb.HibernatePersistence</provider> | |
<!-- JPA entities must be registered here --> | |
<class>com.example.server.domain.Colaborador</class> | |
<class>com.example.server.domain.EntityBase</class> | |
<properties> | |
<property name="hibernate.connection.driver_class" value="org.postgresql.Driver" /> | |
<property name="hibernate.connection.url" value="jdbc:postgresql://localhost:5432/example" /> | |
<property name="hibernate.connection.username" value="postgres" /> | |
<property name="hibernate.connection.password" value="admin" /> | |
<property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect" /> | |
<property name="hibernate.id.new_generator_mappings" value="true" /> | |
</properties> | |
</persistence-unit> | |
</persistence> |
ColaboradorDao.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.example.server.dao; | |
import java.util.List; | |
import javax.persistence.EntityManager; | |
import com.example.server.domain.Colaborador; | |
import com.example.server.resultbean.ColaboradorPagingLoadResultBean; | |
import com.example.server.util.Paginate; | |
import com.google.inject.Inject; | |
import com.google.inject.Provider; | |
import com.google.inject.persist.Transactional; | |
import com.sencha.gxt.data.shared.SortInfoBean; | |
import com.sencha.gxt.data.shared.loader.FilterConfigBean; | |
public class ColaboradorDao implements GenericDao<Colaborador> { | |
@Inject Provider<EntityManager> emProvider; | |
@Inject Paginate<Colaborador> pag; | |
@Transactional | |
public Colaborador persist(Colaborador entity) { | |
emProvider.get().persist(entity); | |
return entity; | |
} | |
public ColaboradorPagingLoadResultBean list(int offset, int limit, List<SortInfoBean> sortInfo, List<FilterConfigBean> filterConfig) { | |
List<Colaborador> list = pag.paginate(Colaborador.class, offset, limit, sortInfo, filterConfig); | |
Long count = pag.count(Colaborador.class); | |
return new ColaboradorPagingLoadResultBean(list, count.intValue(), offset); | |
} | |
@Transactional | |
@Override | |
public void remove(List<Colaborador> entities) { | |
for(Colaborador entity: entities){ | |
emProvider.get().remove(entity); | |
} | |
} | |
} |
In JPA all objects are not persisted before commit the transaction. In Guice there is a useful annotation @Transactional http://code.google.com/p/google-guice/wiki/Transactions that made the method transactional.
ColaboradorPagingLoadResultBean.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.example.server.resultbean; | |
import java.util.List; | |
import com.example.server.domain.Colaborador; | |
import com.sencha.gxt.data.shared.loader.PagingLoadResultBean; | |
@SuppressWarnings("serial") | |
public class ColaboradorPagingLoadResultBean extends PagingLoadResultBean<Colaborador> { | |
public ColaboradorPagingLoadResultBean(List<Colaborador> list, int totalLength, int offset) { | |
super(list, totalLength, offset); | |
} | |
protected ColaboradorPagingLoadResultBean() { | |
super(); | |
} | |
} |
Paginate.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.example.server.util; | |
import java.util.ArrayList; | |
import java.util.List; | |
import javax.persistence.EntityManager; | |
import javax.persistence.TypedQuery; | |
import javax.persistence.criteria.CriteriaBuilder; | |
import javax.persistence.criteria.CriteriaQuery; | |
import javax.persistence.criteria.Predicate; | |
import javax.persistence.criteria.Root; | |
import com.example.server.domain.EntityBase; | |
import com.google.inject.Inject; | |
import com.google.inject.Provider; | |
import com.sencha.gxt.data.shared.SortDir; | |
import com.sencha.gxt.data.shared.SortInfoBean; | |
import com.sencha.gxt.data.shared.loader.FilterConfigBean; | |
public class Paginate<T extends EntityBase> { | |
private Provider<EntityManager> emProvider; | |
private List<FilterConfigBean> filterConfig; | |
@Inject | |
public Paginate(Provider<EntityManager> emProvider){ | |
this.emProvider = emProvider; | |
} | |
public List<T> paginate(Class<T> clazz, int offset, int limit, List<SortInfoBean> sortInfo, List<FilterConfigBean> filterConfig){ | |
this.filterConfig = filterConfig; | |
CriteriaBuilder cb = emProvider.get().getCriteriaBuilder(); | |
CriteriaQuery<T> c = cb.createQuery(clazz); | |
Root<T> r = c.from(clazz); | |
c.where(condition(cb, r).toArray(new Predicate[]{})); | |
if(sortInfo.size() == 0) | |
c.orderBy(cb.desc(r.get("id"))); | |
else{ | |
if(sortInfo.get(0).getSortDir() == SortDir.ASC) | |
c.orderBy(cb.asc(r.get(sortInfo.get(0).getSortField()))); | |
if(sortInfo.get(0).getSortDir() == SortDir.DESC) | |
c.orderBy(cb.desc(r.get(sortInfo.get(0).getSortField()))); | |
} | |
TypedQuery<T> q = emProvider.get().createQuery(c); | |
q.setFirstResult(offset); | |
q.setMaxResults(limit); | |
return q.getResultList(); | |
} | |
public Long count(Class<T> clazz){ | |
CriteriaBuilder cb = emProvider.get().getCriteriaBuilder(); | |
CriteriaQuery<Long> c = cb.createQuery(Long.class); | |
Root<T> r = c.from(clazz); | |
c.where(condition(cb, r).toArray(new Predicate[]{})); | |
c.select(cb.count(r)); | |
return emProvider.get().createQuery(c).getSingleResult(); | |
} | |
private List<Predicate> condition(CriteriaBuilder cb, Root<T> r){ | |
List<Predicate> predicates = new ArrayList<Predicate>(); | |
for(FilterConfigBean s : filterConfig){ | |
if("contains".equals(s.getComparison())){ | |
predicates.add(cb.like(cb.lower(r.<String>get(s.getField())), "%"+s.getValue().toLowerCase()+"%")); | |
} | |
if("gt".equals(s.getComparison())){ | |
predicates.add(cb.gt(r.<Double>get(s.getField()), Double.valueOf(s.getValue()))); | |
} | |
if("lt".equals(s.getComparison())){ | |
predicates.add(cb.lt(r.<Double>get(s.getField()), Double.valueOf(s.getValue()))); | |
} | |
if("eq".equals(s.getComparison())){ | |
predicates.add(cb.equal(r.<Double>get(s.getField()), Double.valueOf(s.getValue()))); | |
} | |
if("on".equals(s.getComparison())){ | |
} | |
if("after".equals(s.getComparison())){ | |
} | |
if("before".equals(s.getComparison())){ | |
} | |
} | |
return predicates; | |
} | |
} |
EntityLocator.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.example.server.locator; | |
import javax.persistence.EntityManager; | |
import com.example.server.domain.EntityBase; | |
import com.google.inject.Inject; | |
import com.google.inject.Injector; | |
import com.google.inject.Provider; | |
import com.google.web.bindery.requestfactory.shared.Locator; | |
/** | |
* Generic @Locator for objects that extend EntityBase | |
*/ | |
public class EntityLocator extends Locator<EntityBase, Long> { | |
@Inject Injector injector; | |
@Inject Provider<EntityManager> emProvider; | |
@Override | |
public EntityBase create(Class<? extends EntityBase> clazz) { | |
return injector.getInstance(clazz); | |
} | |
@Override | |
public EntityBase find(Class<? extends EntityBase> clazz, Long id) { | |
return emProvider.get().find(clazz, id); | |
} | |
/** | |
* it's never called | |
*/ | |
@Override | |
public Class<EntityBase> getDomainType() { | |
throw new UnsupportedOperationException(); | |
// or return null; | |
} | |
@Override | |
public Long getId(EntityBase domainObject) { | |
return domainObject.getId(); | |
} | |
@Override | |
public Class<Long> getIdType() { | |
return Long.class; | |
} | |
@Override | |
public Object getVersion(EntityBase domainObject) { | |
return domainObject.getVersion(); | |
} | |
} |
ColaboradorProxy.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.example.shared.proxy; | |
import com.example.server.domain.Colaborador; | |
import com.example.server.locator.EntityLocator; | |
import com.google.web.bindery.requestfactory.shared.EntityProxy; | |
import com.google.web.bindery.requestfactory.shared.ProxyFor; | |
@ProxyFor(value = Colaborador.class, locator = EntityLocator.class) | |
public interface ColaboradorProxy extends EntityProxy { | |
String getNombres(); | |
void setNombres(String nombres); | |
String getApellidos(); | |
void setApellidos(String apellidos); | |
Integer getEdad(); | |
void setEdad(Integer edad); | |
Long getId(); | |
} |
ColaboradorService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.example.shared.service; | |
import java.util.List; | |
import com.example.server.dao.ColaboradorDao; | |
import com.example.server.requestfactory.InjectingServiceLocator; | |
import com.example.server.resultbean.ColaboradorPagingLoadResultBean; | |
import com.example.shared.proxy.ColaboradorProxy; | |
import com.google.web.bindery.requestfactory.shared.ProxyFor; | |
import com.google.web.bindery.requestfactory.shared.Request; | |
import com.google.web.bindery.requestfactory.shared.RequestContext; | |
import com.google.web.bindery.requestfactory.shared.Service; | |
import com.google.web.bindery.requestfactory.shared.ValueProxy; | |
import com.sencha.gxt.data.shared.SortInfo; | |
import com.sencha.gxt.data.shared.loader.FilterConfig; | |
import com.sencha.gxt.data.shared.loader.PagingLoadResult; | |
@Service(value = ColaboradorDao.class, locator = InjectingServiceLocator.class) | |
public interface ColaboradorService extends RequestContext { | |
Request<ColaboradorProxy> persist(ColaboradorProxy colaborador); | |
Request<Void> remove(List<ColaboradorProxy> colaborador); | |
@ProxyFor(value = ColaboradorPagingLoadResultBean.class) | |
public interface ColaboradorPagingLoadResultProxy extends ValueProxy, PagingLoadResult<ColaboradorProxy> { | |
public List<ColaboradorProxy> getData(); | |
} | |
Request<ColaboradorPagingLoadResultProxy> list(int offset, int limit, List<? extends SortInfo> sortInfo, List<? extends FilterConfig> filterConfig); | |
} |
AppRequestFactory.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.example.shared.service; | |
import com.google.web.bindery.requestfactory.shared.RequestFactory; | |
public interface AppRequestFactory extends RequestFactory { | |
public ColaboradorService colaboradorService(); | |
} |
Internationalization with GWT
I use two properties files
Messages.properties
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
titlePanel = RequestFactory Grid Example | |
add = Add | |
edit = Edit | |
delete = Delete | |
save = Save | |
cancel = Cancel | |
addColaborador = Add Colaborador | |
editColaborador = Edit Colaborador | |
deleteColaborador = Delete Colaborador | |
infoUser = User Information | |
firstName = First Name | |
lastName = Last Name | |
age = Age | |
saveSuccessful = Saved successful | |
deleteSuccessful = Deleted successful | |
deleteConfirm = Are you sure to delete {0}? | |
deleteAllConfirm = Are you sure to delete selected records? |
Messages_es_PE.properties
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
titlePanel = RequestFactory Grid Ejemplo | |
add = Agregar | |
edit = Editar | |
delete = Eliminar | |
save = Guardar | |
cancel = Cancelar | |
addColaborador = Nuevo Colaborador | |
editColaborador = Editar Colaborador | |
deleteColaborador = Eliminar Colaborador | |
infoUser = Información de Usuario | |
firstName = Nombres | |
lastName = Apellidos | |
age = Edad | |
saveSuccessful = Se guardo correctamente | |
deleteSuccessful = Se elimino correctamente | |
deleteConfirm = ¿Esta seguro que desea eliminar al colaborador {0}? | |
deleteAllConfirm = ¿Esta seguro que desea eliminar los registros seleccionados? |
Go to project path and write the command mvn gwt:i18n
Is generated in target/generated-sources
You should add to build path right click on generated-sources/gwt folder Build Path --> Use as Source Folder.
You can use command mvn gwt:run to launch the Hosted Mode.
Buenas Noches, estoy comenzando con java y gxt,estoy tratando de correr el ejemplo pero tengo un error en eclipse que dice "the import com.example.client.Messages can not be resolved"
ResponderEliminarme podrias orientar.
Gracias
Eso quiere decir que la interfaz Messages no esta en el build path.
ResponderEliminarMessages es auto generado con maven. Debes ir al directorio del proyecto con la linea de comandos y escribir mvn gwt:i18n
Luego usando eclipse le das click derecho al folder target/generated-sources/gwt del menu contextual seleccionas Build Path --> Use as Source Folder.
Puedes ejecutar el proyecto con mvn gwt:run
Buenas, he estado experimentando en particular con lo siquiente:
ResponderEliminar- RequestFactory
- GXT
- Grid con Paginacion.
Este ejemplo ha sido muy util. Estoy teniendo un ligero problema y es ya practicamente al final del proceso cuando la data final va a ser devuelta al cliente. En particular, estoy devolviendo un objeto en el servidor de tipo:
public CardPagingLoadResultBean findAll() {
...
return new CardPagingLoadResultBean(cards, cards.size(), 1);
}
Despues de investigar, RequestFactory esta esperando una lista y evidentemente este objeto no lo es, lo cual causa la excepcion que aparece debajo.
Cualquier idea o ayuda en esta area seria muy agradecida.
Alguien ha tenido el mismo problema o tiene idea de que se pueda hacer en este caso?
Gracias.
Aug 16, 2012 12:39:29 PM com.google.web.bindery.requestfactory.server.RequestFactoryServlet doPost
SEVERE: Unexpected error
java.lang.ClassCastException: interface java.util.List
at java.lang.Class.asSubclass(Class.java:3018)
at com.google.web.bindery.requestfactory.server.ResolverServiceLayer.resolveClientType(ResolverServiceLayer.java:69)
at com.google.web.bindery.requestfactory.server.ServiceLayerDecorator.resolveClientType(ServiceLayerDecorator.java:142)
at com.google.web.bindery.requestfactory.server.ServiceLayerDecorator.resolveClientType(ServiceLayerDecorator.java:142)
at com.google.web.bindery.requestfactory.server.ServiceLayerDecorator.resolveClientType(ServiceLayerDecorator.java:142)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
En GXT Grid se usa el objeto CardPagingLoadResultBean(cards, count, 0) para envolver la lista cards y agregarle informacion adicional como el total de registros count (no confundir con el tamaño de la lista) y el offset.Por ejemplo si quiero mostrar 30 cards por página y el total de cards es 100 entonces count = 100 y limit=30, offset = 0, 30, 60, etc.
EliminarSi vas a paginar los cards el código sería el siguiente
public CardPagingLoadResultBean list(int offset, int limit, List sortInfo, List filterConfig) {
List list = pag.paginate(Card.class, offset, limit, sortInfo, filterConfig);
Long count = pag.count(Card.class);
return new CardPagingLoadResultBean(list, count.intValue(), offset);
}
y el service está esperando el objeto CardPagingLoadResultProxy
@Service(value = CardDao.class, locator = InjectingServiceLocator.class)
public interface CardService extends RequestContext {
...
@ProxyFor(value = CardPagingLoadResultBean.class)
public interface CardPagingLoadResultProxy extends ValueProxy, PagingLoadResult {
public List getData();
}
Request list(int offset, int limit, List sortInfo, List filterConfig);
}
el método getData() lo usa GXT para obtener la lista de CardProxy.
En RequestFactory el objeto entidad Card del servidor tiene su correspondiente objeto cliente CardProxy. Así en el servidor está el objeto CardPagingLoadResultBean y en el cliente es CardPagingLoadResultProxy.
This resource was extremely helpful, and the example project was an excellent starting point for me. I am now figuring out how to protect these requestfactory calls with authentication with something like guice method interception similar to what is done here:
ResponderEliminarhttp://dzone.com/snippets/secure-your-gwt-app-easily
Not sure if you, or anyone else reading this blog had had any experience with this.
Este comentario ha sido eliminado por el autor.
ResponderEliminarApache shiro
ResponderEliminar!-- apache shiro -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.2.1</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
<version>1.2.1</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-guice</artifactId>
<version>1.2.1</version>
</dependency>
Guice and Shiro Aop
https://gist.github.com/poseidonjm/5171827
Shiro Web Module
https://gist.github.com/poseidonjm/5171913
Authentication Filter
https://gist.github.com/poseidonjm/5171942
Custom Realm for dinamic roles
https://gist.github.com/poseidonjm/5171969
Custom realm for static roles
https://gist.github.com/poseidonjm/5171990
Util Class
https://gist.github.com/poseidonjm/5172009
Reference
http://shiro.apache.org/java-authorization-guide.html
Annotate your methods
@RequiresPermissions("ACTIONS:DELETE")
@Transactional
public void remove(Action entity) {
}
or
@RequiresRoles("OPERARIO")
or
@RequiresAuthentication
Greetings
Ooh I read this too late. I don't have an authorization solution yet, so this is extremely helpful. I ended up spending a lot of time figuring just the authentication piece out. I think you will get good feedback if you do a blogpost about this.
ResponderEliminarWent through some of the files you provided, maybe I didn't spend enough time - it doesn't really explain how you would handle the client with things like session timeouts etc. - the solution I have below deals specifically with session timeouts. Protecting request factory servlets gets me only partially there. I still need to know what to do when session times out, or when the client gets an access denied message.
Here is what I ended up doing:
http://pastebin.com/mQQMMyAk
And then:
http://pastebin.com/bmq7azW6
And when login is successful:
http://pastebin.com/JqwghAHh
Here is a revised version of the DefaultRequestTransport subclass. The pending requests queue was not getting contstructed correctly.
ResponderEliminarhttp://pastebin.com/ir0wFbLU
I use very similar RequestTransport https://gist.github.com/poseidonjm/5275288 and use it for
ResponderEliminar@Provides
@Singleton
public AppRequestFactory createRequestFactory(EventBus eventBus, MyDefaultRequestTransport requestTransport) {
AppRequestFactory factory = GWT.create(AppRequestFactory.class);
factory.initialize(eventBus, requestTransport);
return factory;
}
I use a login.jsp for login form using Apache Shiro. When get a SC_UNAUTHORIZED response I reload the page Window.Location.reload(); and because the session is expired Shiro redirects to login.jsp
I am able to run this application on eclipse. We use IntelliJ Idea, can you tell me why the application do not run. Intellij do not able to find new generated files.
ResponderEliminarImran