Client Implementation - ManfredTremmel/gwt-bean-validators-example GitHub Wiki

Client Part

The client part is based on a stack of software which enables us to programm client part in Java which is translated into JavaScript to be executed in common web browsers. We do have a single page model where only parts are changed which change, building the html code and rendering is done on client side, this scales better then server side page generation and keeps trafic low between client and server.

The following components are used:

  • GWT provides the java to java script compiler and a hugh class library for client side coding
  • from GWTP I use the MVP implementation with much reduced boiler plate code in comparison with the GWT MVP model. Also the REST dispatcher is used for communication with the server
  • gwt-bean-validators is one of my projects, it tries to fix the limits of bean validations on client side in GWT, it also provides a bunch of validation routines, an editor which includes automaticly validation and decorators which display validation results in the form
  • gin brings injection to the client
  • gwt-mt-widgets is also one of my projects, it contains a number of usefull input widgets
  • gwtp-dynamic-navigation another project I've created, it makes it easy to build a navigation which changes, depending on the userrights. It uses GWTP gatekeepers to show or hide menu entries, when user is allowed or disallowed to view the corresponding page
  • gwtp-spring-integration lot of classes to help integrating GWTP on client side with spring on server side
  • gwt-pushstate is used to get readable urls. By default the "pages" of a single page application are accessed by tokens behind hash tags (#), the html5 pushstate allows to use normal urls, gwt-pushstate makes it easy to include it into gwt applications. Initial gwt-pushstate was developt by Johannes Barop and forked by Richard Wallis to build a GWT >= 2.6 compatible implementation, where I've forked it to fix some bugs and ad new features.
  • gwt-commons-lang3, a gwt port I've done from the Apache commons-lang3 library. Why? Have you ever done null save string comparison without StringUtils? Therfore!
  • gwt-commons-validator, a gwt port I've done from the Apache commons-validator with lot of validation routines used by gwt-bean-validators

Project organization

In a lot of projects all views and presenters are grouped in packages, I don't think this is useful, when projects grow. To add all the code of one page into one package, with the same package name as the page token, makes it much easier to find the code to a page:

Validation

Client side validation

Input validation on client side is a big waist of time and error-prone to reimplement the same tests in java script you've already implemented on server side (never trust the client!). Using JSR-303 bean validation annotations in the Model makes it easy to validate on server- and client side. Let's take a look to the sepa bank account, the model contains the validation annotations, the validation logic is implemented in the BeanValidationEditorDriver, to use it, simply take a look into the view:

The BeanValidationEditorDriver driver replaces the gwt Editor driver to bind model with input form, declare a interface:

interface Driver extends BeanValidationEditorDriver<SepaData, SepaViewGwtImpl> {
}

it's injected in constructor (or if you don't use gin, use GWT.create()) and you can give it a submit button, and a handler which is called, whenever the submit is sent and validation is ok. It also takes care you can submit the form using the return/enter key (you can disable this using "this.driver.setSubmitOnReturn(false);"). You also can advise the editor, when to validate data, default is a "on the fly" validation, when ever a character is inserted or value changes, with "this.driver.setCheckTime(CheckTimeEnum);" you can change this:

@Inject
public SepaViewGwtImpl(final Driver pdriver, final Binder puiBinder) {
  super();
  this.initWidget(puiBinder.createAndBindUi(this));
  this.driver = pdriver;
  this.driver.initialize(this);
  this.driver.setSubmitButton(this.sepaButton);
  this.driver.addFormSubmitHandler(this);
  ...

now the onFormSubmit method (you have to implement) is always called, when the form data are valid and submited, so you can be sure, you do have correct data on this place. The input form is synced with the model, so you needn't take date from event, just work with the model.

@Override
public void onFormSubmit(final FormSubmitEvent<SepaData> pevent) {
  this.presenter.tryToSend();
}

Handling results from server side validation

When ever the model fails validation on server side, the RestErrorHandler returns validation results to the client with a http 400 state, the error handler in AbstractRestCallback takes the validation result and sends it to the view, where it's put into the editor driver, which handles and delegates it. And that's all you have to implement:

@Override
public void setConstraintViolations(final ArrayList<ConstraintViolation<?>> pvalidationErrorSet) {
  this.driver.setConstraintViolations(pvalidationErrorSet);
}

REST communication with the server

The model on client and server side is the same, so it has only to be created once, but for each REST controller implemented on server side, we do have to build a interface. For the sepa controller (simplified version):

@RestController
@RequestMapping(value = AppResourcePaths.SEPA, produces = MediaType.APPLICATION_JSON_VALUE)
public class SepaController {
  @RequestMapping(method = RequestMethod.POST)
  @PermitAll
  public void checkSepa(@Valid @RequestBody final SepaData psepaData) {
    ...
  }
}

a interface is created as follows:

@Path(AppResourcePaths.SEPA)
public interface SepaRestService {
  @POST
  RestAction<Void> checkSepa(final SepaData psepaData);
}

as you can see, I use constants for path (and argument) names, to be sure, they don't differ. The result is capsulated in a RestAction<> and javax.ws annotations are used instead of spring annotations, the rest is equal. To use the interface you can inject it and use with the rest dispatcher, as you can see in the presenter:

...
private final RestDispatch dispatcher;
private final SepaRestService sepaService;

@Inject
public SepaPresenter(..., final RestDispatch pdispatcher,
    final SepaRestService psepaService, ...) {
  ...
  this.dispatcher = pdispatcher;
  this.sepaService = psepaService;
  ...
}
...
public final void tryToSend() {
  this.dispatcher.execute(this.sepaService.checkSepa(this.sepaData),
      RestCallbackBuilder.build(this.getView(), this.sepaData, this.session,
          presult -> this.getView().showMessage(this.constants.messageSepaOk())));
}

The RestCallbackBuilder creates a RestCallback with included error handling, the error messages are given to the view. Only the success path is handled by owr own, in this case a ok message is given to the view.

Security

CSRF/XSRF protection

The GWTP rest dispatcher makes it realy easy to add CSRF/XSRF protection, declare a service module which installs a XSRF-TokenHeader and binds it to the security cookie. Install it in your application gin configuration and the dispatcher takes care to send the security header with each request.

Login and protect webservices with spring security

The login is done on server side with Spring Security, but this doesn't matter on client side, it's simply a rest service call in the LoginPresenter which returns user data on success. The user data is saved in a client side session and each time it changes a ChangeUserEvent is thrown to inform each component about this change. If you load the page, first thing what's done is asking the server, if a session exists and loading user data if it dose. So we can take up existing session and work on. But what happens when session times out? The services which are only accessable for authorized users are secured by Spring Security, they will not return data, they will end with http state 401 (unauthorized). The AbstractRestCallback handles the state and starts a reload of the user data, as we don't have any user after session timout, the client side user data are also deleted and user is redirected to login page.

Protect pages from access of non authorized users

To protect pages from access of unauthorized users, the GWTP gatekeepers are used. This are simple classes which implement Gatekeeper interface which provides the method "boolean canReveal()" which has to return true if access is allowed and false if not. I've added the LoggedInGatekeeper as default gatekeeper, each page without a annotated gatekeeper uses it and can only be accessed after log in. The same mechanism is used to hide and show navigation entries. The navigation entries are defined in MyNavigationStructure and each one is protected by a gatekeeper, which decides if a menu entry is shown to the user. The menu catches ChangeUserEvent and rebuilds the structure whenever user changes.

⚠️ **GitHub.com Fallback** ⚠️