Monday, May 14, 2012

Beyond Hello - Input validation

Input validation

If you have gotten Beyond Hello it should be time to add some input validation.
It should not be possible for the user to produce invalid input. 
For example, it should not be possible to press the Save  button before all input fields have been filled in correctly. Vaadin has a number of built-in validators that I will use to build simple a Screen Validator.

Initially, the OK button should be disabled. It should only come alive when the two fields Name and Email are filled in correctly.
We will require name to be between 3 and 32 characters long, and email should be on the form "a@b.c" where "b.c" is some domain.
When both fields are filled in, the OK button should become enabled, and if we go back and erase or change the field contents, the Screen Validator should maintain the state of the OK button, turning it on and off in response to the validation state.

To know whether a screen is valid, we need to know if one of the widgets in it is invalid. In other words, the screen is valid if all the input fields it contains are valid.
This means that the Validator must know which widgets to validate.
Let's create a class to keep track of a widget's validation state. This is not needed to manage the OK button state, but can be useful in providing hints to the user, and it can be useful for debugging.

Here, there is a 'status line' telling the user which field failed validation. It shows the name of the field and its validation message. We create a class to hold this information:
private class ValidationState{
String name;
String validationMessage="";
Boolean valid = true;
public ValidationState(String name) {
this.name = name;
}
public String getName() {
return name;
}
public String getValidationMessage() {
return validationMessage;
}
public void setValid(Boolean valid) {
this.valid = valid;
}
public void setValidationMessage(String validationMessage) {
this.validationMessage = validationMessage;
}
The contents of the status line is name + ":" + validationMessage.
public class ScreenValidator {
private class ValidationState{
                 ...
}
private HashMap<Field, ValidationState> hashMapField = new HashMap<Field, ValidationState>();
Each field that is added to the ScreenValidator gets stored in a HashMap.
We also need a callback structure to let the parent widget know whether our screen is valid or not.
To this end, we define a BooleanEvent and a BooleanEventHandler.
package com.example.vaadin.events;
public class BooleanEvent {
private Boolean value;
public BooleanEvent(Boolean value) {
this.value = value;
}
public Boolean getValue() {
return value;
}
}
This event type simply holds a true/false value, we will let true indicate valid, false will indicate not valid. Then we define the event handler:
package com.example.vaadin.events;
public abstract class BooleanEventHandler {
public abstract void eventOccurred(BooleanEvent evt);
}
We now have all the building blocks we need in order to put it all together.
package com.example.vaadin.view;
import com.example.vaadin.events.BooleanEvent;
import com.example.vaadin.events.BooleanEventHandler;
import com.vaadin.ui.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map.Entry;
public class ScreenValidator {
private class ValidationState{
String name;
String validationMessage="";
Boolean valid = true;
public ValidationState(String name) {
this.name = name;
}
public String getName() {
if (name == null){
return "No Name";
}
return name;
}
public String getValidationMessage() {
if (validationMessage == null){
return "";
}
return validationMessage;
}
public void setValid(Boolean valid) {
this.valid = valid;
}

public void setValidationMessage(String validationMessage) {
this.validationMessage = validationMessage;
}
}
private HashMap<Field, ValidationState> hashMapField = new HashMap<Field, ValidationState>();
private Boolean valid = false;
private ArrayList<BooleanEventHandler> listValidHandler = new ArrayList<BooleanEventHandler>();
private String validationError = "";
public void addValidationHandler(BooleanEventHandler h) {
listValidHandler.add(h);
}
public void removeValidationHandler(BooleanEventHandler h) {
listValidHandler.remove(h);
}
void callbackValid() {
for (BooleanEventHandler h : listValidHandler) {
h.eventOccurred(new BooleanEvent(valid));
}
}
/**
* Add a component to the validator
* @param c The component you are adding validation for.
* @param name The name of the field, typically the same as the field's prompt
*/
public void addComponent(Field c, String name) {
hashMapField.put(c, new ValidationState(name));
}
public void removeAllComponents(){
hashMapField.clear();
}
/**
* Validate - Generate callback if change in  valid status
*/
public void validate() {
Boolean oldValid = valid; // Use this is you only want to callback on changes in validation status
valid = true;
validationError = null;
String strDebug = this.getClass().getSimpleName() + ".validate() :-";
for (Entry<Field, ValidationState> e : hashMapField.entrySet()) {
ValidationState validationState = e.getValue();
Boolean validField = true; // DEBUG
Field f = e.getKey();
if (!f.isVisible()) {
continue;
}
try {
f.validate();
} catch (Exception ex) {
if (validationError == null) {
validationError = validationState.getName() + ":" + ex.getMessage();
}
validationState.setValid(false);
validationState.setValidationMessage(ex.getMessage());
valid = false;
validField = false;
}
strDebug += "  " + validationState.getName() + ":" + f.getClass().getSimpleName() + ":" + f.toString() + ":" + validField;
}
strDebug += " valid=" + valid + ":" + validationError;
System.out.println(strDebug); // Track validation state
//if (oldValid != valid){ // Only call back if change
callbackValid();
//}
}
public Boolean isValid() {
return valid;
}
public String getValidationError() {
if (validationError == null){
return "";
}
return validationError;
}
}

To use the ScreenValidator, you first instantiate it, then add all the Fields you want validated.
screenValidator.addComponent(textFieldName, "Name");
screenValidator.addComponent(textFieldEmail, "Email");
 Now, ScreenValidator knows which Fields to validate.
To see it all in action, download the project from Ramsli.org


Wednesday, March 21, 2012

Beyond Hello, world

Many application frameworks come with a 'Hello, world' application, and almost every time I have built it and stared at it, wondering what do do next.
I have given up on Struts, JSF, XSLT and a few others because I could not for the life of me figure out how to manage application complexity using any of them.
If you ever experienced something like that, then this may just be for you.
Enter Vaadin
Finally, a web application framework has emerged that lets me do what I want!
Even though I am a total newbie when it comes to Vaadin, I hope to show you how to create a complex Model-View-Controller application with several layers of views, at least in principle.

I will start by showing step-by-step how to get Vaadin up and running, all the way to the 'Hello' app, and I will continue to show you how I proceeded to add sub-views to my application.

Preparation
In the following, you are expected to know some Java and to know how to extend your favorite Java IDE with plugins.
I use Netbeans, even though I know I should be using Eclipse.
If you are an Eclipse user, some parts may easier than they are with Netbeans, especially regarding plugins.

You need:

  1. A Java IDE

  2. Vaadin

  3. You may also like a plugin

After downloading Vaadin and installing the plugin, you should be good to go, creating the first Vaadin application.
Create a new project, select Web Application.


Call it VaadinFirst



Then, just accept the defaults in the next screen


The next screen lets you choose a framework, and if your plugin installation has gone OK, you should tick 'Vaadin' and accept the proposed defaults.



Now, the project should appear.


Open the file called MyApplication.java and verify that it looks something like this:

/*
* MyApplication.java
*
* Created on March 21, 2012, 12:32 PM
*/

package com.example.vaadin;

import com.vaadin.Application;
import com.vaadin.ui.Label;
import com.vaadin.ui.Window;
import java.util.logging.Logger;
/**
*
* @author atle
* @version
*/

public class MyApplication extends Application {
private static final long serialVersionUID = 1L;

@Override
public void init() {
Window mainWindow = new Window("MyApplication");
Label label = new Label("Hello Vaadin user");
mainWindow.addComponent(label);
setMainWindow(mainWindow);
}
private static final Logger LOG = Logger.getLogger(MyApplication.class.getName());

}


If you have set up an application server (I use Glassfish), you should be able to run the project.


If the project does not run, you may have to set up an application server.

If you do not see the application VaadinFirst, try removing the server and adding it back in.

Adding some code
First, add packages model and view, we will use MyApplication.java as controller.



Under view, create a class PanelPersonalInfo.



Edit PanelPersonalInfo.java to look something like this:

/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package com.example.vaadin.view;

import com.vaadin.ui.Panel;
import java.util.logging.Logger;

/**
*
* @author atle
*/
public class PanelPersonalInfo extends Panel {
private static final Logger LOG = Logger.getLogger(PanelPersonalInfo.class.getName());
private static final long serialVersionUID = 1L;

}



Create a class PersonalInfo, something like shown below:

/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package com.example.vaadin.model;

import java.util.logging.Logger;

/**
*
* @author atle
*/
public class PersonalInfo {
private static final Logger LOG = Logger.getLogger(PersonalInfo.class.getName());
private String name;
private String email;

/**
* @return the name
*/
public String getName() {
return name;
}

/**
* @param name the name to set
*/
public void setName(String name) {
this.name = name;
}

/**
* @return the email
*/
public String getEmail() {
return email;
}

/**
* @param email the email to set
*/
public void setEmail(String email) {
this.email = email;
}
}


Do not add any methods except getters and setters, possibly a toString() for debugging purposes.
If you need a class that supports complex methods, create a class PersonalInfoHelper like this:

/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package com.example.vaadin.model;

import java.util.logging.Logger;

/**
*
* @author atle
*/
public class PersonalInfoHelper {
private static final Logger LOG = Logger.getLogger(PersonalInfoHelper.class.getName());
private PersonalInfo personalInfo;

public PersonalInfoHelper(PersonalInfo personalInfo) {
this.personalInfo = personalInfo;
}

}


Throw them both in the model layer.
The reason for using a helper class to access an entity it to avoid clogging up the class when using web services for storage/retrieval.

Now, extend PanelPersonalInfo with some event handling, define an event and a handler. Create a package called events and define the two classes below:

public class PersonalInfoEvent{
private PersonalInfoHelper personalInfoHelper;

public PersonalInfoEvent(PersonalInfoHelper personalInfoHelper) {
this.personalInfoHelper = personalInfoHelper;
}

/**
* @return the personalInfoHelper
*/
public PersonalInfoHelper getPersonalInfoHelper() {
return personalInfoHelper;
}

}

public abstract class PersonalInfoEventHandler{
public abstract void eventOccurred(PersonalInfoEvent evt);
}



Then add a handler list to PersonalInfoPanel.


/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package com.example.vaadin.view;

import com.example.vaadin.model.PersonalInfoHelper;
import com.vaadin.ui.Panel;
import java.util.ArrayList;
import java.util.logging.Logger;

/**
*
* @author atle
*/
public class PanelPersonalInfo extends Panel {
private static final Logger LOG = Logger.getLogger(PanelPersonalInfo.class.getName());
private static final long serialVersionUID = 1L;

private ArrayList handlerList = new ArrayList();


public void addHandler(PersonalInfoEventHandler h){
handlerList.add(h);
}

public void removeHandler(PersonalInfoEventHandler h){
handlerList.remove(h);
}

}


PanelPersonalInfo now has what it takes to report back to the controller, in our case, MyApplication.


Widgets!

For MyApplication to be able to display an instance of PersonalInfo, we let PanelPersonalInfo contain an instance of PersonalInfoHelper.
We then create a method setPersonalInfoHelper(PersonalInfoHelper pih) that updates the widgets.
We will also justify the use of PersonalInfoHelper by modifying PersonalInfo a little bit:

public class PersonalInfo {
private static final Logger LOG = Logger.getLogger(PersonalInfo.class.getName());
private String firstName;
private String middleName;
private String lastName;
private String email;

...


PersonalInfo no longer has a method called getName(), so PersonalInfoHelper stitches together the name from its components. It also tries to 'parse' a name string and spilt it into first,middle and last name, or rather, that is left as an exercise :)


public class PersonalInfoHelper {
private static final Logger LOG = Logger.getLogger(PersonalInfoHelper.class.getName());
private PersonalInfo personalInfo;

public PersonalInfoHelper(PersonalInfo personalInfo) {
this.personalInfo = personalInfo;
}

public String getName() {
String rv = personalInfo.getFirstName();
if (personalInfo.getMiddleName() != null && ! personalInfo.getMiddleName().isEmpty()){
rv += " " + personalInfo.getMiddleName();
}
rv += " " + personalInfo.getLastName();
return rv;
}


You should create a class that stores and retrieves data, and put that in the model package.

Seeing it in action!
I will now show the entire PanelPersonalInfo class with a few comments.

/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package com.example.vaadin.view;

import com.example.vaadin.events.PersonalInfoEvent;
import com.example.vaadin.events.PersonalInfoEventHandler;
import com.example.vaadin.model.PersonalInfo;
import com.example.vaadin.model.PersonalInfoHelper;
import com.vaadin.event.FieldEvents;
import com.vaadin.event.FieldEvents.TextChangeEvent;
import com.vaadin.ui.GridLayout;
import com.vaadin.ui.Label;
import com.vaadin.ui.Panel;
import com.vaadin.ui.TextField;
import java.util.ArrayList;
import java.util.logging.Logger;

/**
*
* @author atle
*/
public class PanelPersonalInfo extends Panel {

private static final Logger LOG = Logger.getLogger(PanelPersonalInfo.class.getName());
private static final long serialVersionUID = 1L;
private PersonalInfoHelper personalInfoHelper;
private ArrayList handlerList = new ArrayList();
private Label labelName;
private TextField textFieldName;
private Label labelEmail;
private TextField textFieldEmail;

// Whenever we set a value on a widget, we trigger the widget's event handlers.
// Since we want to process the data and then set them back into the panel, we need to disable
// callback on all the setXXX() functions in out panel (see the callBack() function.)
private Boolean disableCallback = false;

public PanelPersonalInfo(String caption) {
super(caption);
createWidgets();
}

private void createWidgets() {
labelName = new Label("Name");
textFieldName = new TextField();
textFieldName.setImmediate(true);
textFieldName.addListener(new FieldEvents.TextChangeListener() {

private static final long serialVersionUID = 1L;

@Override
public void textChange(TextChangeEvent event) {
personalInfoHelper.setName(event.getText());
}
});
labelEmail = new Label("Email");
textFieldEmail = new TextField();
textFieldEmail.setImmediate(true);
textFieldEmail.addListener(new FieldEvents.TextChangeListener() {

private static final long serialVersionUID = 1L;

@Override
public void textChange(TextChangeEvent event) {
personalInfoHelper.setEmail(event.getText());
callBack(); // If we had a PanelButtons as a sub-panel, we would only callBack when user pressed one.
}
});
GridLayout layout = new GridLayout(2, 2);
layout.addComponent(labelName);
layout.addComponent(textFieldName);
layout.addComponent(labelEmail);
layout.addComponent(textFieldEmail);
addComponent(layout);
}

public void addHandler(PersonalInfoEventHandler h) {
handlerList.add(h);
}

public void removeHandler(PersonalInfoEventHandler h) {
handlerList.remove(h);
}

/**
* Callback when the user makes a change
*/
private void callBack() {
if (!disableCallback) {
for (PersonalInfoEventHandler h : handlerList) {
h.eventOccurred(new PersonalInfoEvent(personalInfoHelper));
}
}
}

/**
* @return the personalInfoHelper
*/
public PersonalInfoHelper getPersonalInfoHelper() {
// Here, you could get the data from the widgets before returning
return personalInfoHelper;
}

/**
* @param personalInfoHelper the personalInfoHelper to set
*/
public void setPersonalInfoHelper(PersonalInfoHelper personalInfoHelper) {
this.personalInfoHelper = personalInfoHelper;
disableCallback = true; // Don't bubble events back up to parent. Chances are that the parent's
// event handling results in this method being called, resulting in
// a funny, blinking screen :)
textFieldName.setValue(personalInfoHelper.getName());
textFieldEmail.setValue(personalInfoHelper.getEmail());
disableCallback = false; // We manipulated the widgets, turn events back on.
}
}


All that remains, is to hook it up to MyApplication:

/*
* MyApplication.java
*
* Created on March 21, 2012, 12:32 PM
*/
package com.example.vaadin;

import com.example.vaadin.events.PersonalInfoEvent;
import com.example.vaadin.events.PersonalInfoEventHandler;
import com.example.vaadin.model.PersonalInfo;
import com.example.vaadin.model.PersonalInfoHelper;
import com.example.vaadin.view.PanelButtons;
import com.example.vaadin.view.PanelPersonalInfo;
import com.vaadin.Application;
import com.vaadin.ui.Window;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
*
* @author atle
* @version
*/
public class MyApplication extends Application {

private static final long serialVersionUID = 1L;
PersonalInfoHelper personalInfoHelper = new PersonalInfoHelper(new PersonalInfo()); // Create a Database class to store/retrieve
@Override
public void init() {
Window mainWindow = new Window("First Vaadin Application");
PanelPersonalInfo panelPersonalInfo = new PanelPersonalInfo("Personal Information");
panelPersonalInfo.addHandler(new PersonalInfoEventHandler() {

@Override
public void eventOccurred(PersonalInfoEvent evt) {
LOG.log(Level.INFO, "Got a PersonInfo event:{0} with data:{1}", new Object[]{evt.toString(), evt.getPersonalInfoHelper()});
}
});
personalInfoHelper.setName("Jan Atle Ramsli");
personalInfoHelper.setEmail("jaramsli@gmail.com");
panelPersonalInfo.setPersonalInfoHelper(personalInfoHelper);
mainWindow.addComponent(panelPersonalInfo);

PanelButtons panelButtons = new PanelButtons();

mainWindow.addComponent(panelButtons);
setMainWindow(mainWindow);
}
private static final Logger LOG = Logger.getLogger(MyApplication.class.getName());
}


Run the app, and you should see something like this:



What now?

Now you can create an address panel, and then you can let one person have multiple addresses, eg. home, work, billing, etc. For each Address, you could create a PanelAddress and call setAddress(AddressHelper ah) on it.
To contain the addresses you could use a TabSheet
The address panels would then bubble the address part of PersonalInfo up PanelPersonalInfo.
A Simplification
For a simple application, you could replace the handler list with abstract methods.

public abstract class LoginScreen extends Window {

private MyApplication myApplication;
private static final long serialVersionUID = 1L;
private static final Logger LOG = Logger.getLogger(LoginScreen.class.getName());
private String userName;
private String password;
private TextField textFieldUserName;
private TextField textFieldPassword;
private Button buttonLogin;
private Button buttonCancel;
private Label labelPopup;

public abstract void ok(String userName, String password);

public abstract void cancel();

public abstract void sendNewPassword(String email);
...
buttonLogin.addListener(new ClickListener() {

private static final long serialVersionUID = 1L;

@Override
public void buttonClick(ClickEvent event) {
setVisible(false);
ok(userName,password);
}
});

The next step would be to add some Input Validation to your app ...

Disclaimer
This example was thrown together in a hurry, so it will be full of errors - but I may find the time to get back to it.

Watch this space :)

Atle jaramsli (at) gmail.com