Written by
  • email
  • twitter
  • linkedin
  • linkedin
Apache Syncope already provides powerful, tunable attribute validators, enforced by the Core back-end: what about customizing front-end validation?
As most of you already know, if ever meet Syncope world, one of the main challenges that Syncope developers faced and often face is provide extreme customizability. Though the main customization features are provided and developed at the core level (to have an idea of what I'm talking about please visit this ) even console provides some customization features. We are going to introduce a way to customize frontend validation in the console module.

First of all let's define our custom validator class

public class DateConsistencyValidator extends AbstractFormValidator {

    private static final long serialVersionUID = -4661147385331769440L;

    private String dateComponent1;

    private String dateComponent2;

    private final FormComponent[] components;

    public DateConsistencyValidator(final String dateComponent1, final String dateComponent2) {
        this.dateComponent1 = dateComponent1;
        this.dateComponent2 = dateComponent2;
        components = new FormComponent[0];

    public void validate(final Form form) {
        AjaxDateFieldPanel component1 = getField(form, AjaxDateFieldPanel.class, dateComponent1);
        AjaxDateFieldPanel component2 = getField(form, AjaxDateFieldPanel.class, dateComponent2);

        if (component1.getField().getConvertedInput() != null
                && component2.getField().getConvertedInput() != null
                && component1.getField().getConvertedInput().after(component2.getField().getConvertedInput())) {

            Map vars = new HashMap<>();
            vars.put("field1Name", component1.getName());
            vars.put("field2Name", component2.getName());
            // use the following line if you do not want to take message from properties file
            // form.error("Value of field ${field1Name} is not equal to ${field2Name}", vars);
            form.error(component1.getString("date.consistency.validationError"), vars);

    public FormComponent[] getDependentFormComponents() {
        return components;

    protected > T getField(
            final Form form,
            final Class clazz,
            final String name) {
        T component = form.visitChildren(clazz,
                new IVisitor() {

            public void component(final T arg0, final IVisit arg1) {
                if (name.equalsIgnoreCase(StringUtils.isBlank(arg0.getName())
                        ? arg0.getField().getLabel().getObject()
                        : arg0.getName())) {

        return component;


Then let's define a custom wizard builder class that uses the custom validator and extends default UserWizardBuilder provided by Syncope

public class UserWithValidationWizardBuilder extends UserWizardBuilder {

    private static final long serialVersionUID = 1680007693565519842L;

    public UserWithValidationWizardBuilder(
            final List anyTypeClasses,
            final UserFormLayoutInfo formLayoutInfo,
            final PageReference pageRef) {

        super(anyTypeClasses, formLayoutInfo, pageRef);

    public UserWithValidationWizardBuilder(
            final UserTO previousUserTO,
            final UserTO userTO,
            final List anyTypeClasses,
            final UserFormLayoutInfo formLayoutInfo,
            final PageReference pageRef) {

        super(previousUserTO, userTO, anyTypeClasses, formLayoutInfo, pageRef);

    protected WizardModel buildModelSteps(final AnyWrapper modelObject, final WizardModel wizardModel) {
        super.buildModelSteps(modelObject, wizardModel);

        if (formLayoutInfo.isPlainAttrs()) {
            PlainAttrs plainAttrs = getWizardStep(PlainAttrs.class, wizardModel);
            // add form validators
            if (plainAttrs != null) {
                plainAttrs.add(new DateConsistencyValidator("loginDate", "logoutDate"));

        return wizardModel;

    protected  T getWizardStep(final Class clazz, final WizardModel wizardModel) {
        for (WizardModel.ICondition condition : wizardModel.getConditions()) {
            if (clazz.isInstance(condition)) {
                return clazz.cast(condition);
        return null;


Please notice the simplicity: in order to enable validation on two (or more, depends on validator implementation) you just need to add the following line:

plainAttrs.add(new DateConsistencyValidator("mydate1", "mydate2"));

mydate1 and mydate2 are the names of existing Syncope schemas and reference user attributes.
Moreover, in order to get the message from properties file (like shown in the example) add a property to the file referenced by the attribute panel, for example AbstractFieldPanel.properties, located under


The line should look like this:

date.consistency.validationError=${field1Name} shall not be after ${field2Name}

Disclaimer: The provided implementation is only a sample to check dates consistency (fail if mydate1 is after mydate2), you can define whatever form validator by overriding AbstractFormValidator class provided by Apache Wicket.

Where to place such files?

If you are using a project generated from archetype place the wizard builder class in


a and the custom validator(s) in


Finally build, deploy and restart you application server hosting Syncope.

How do I enable the custom wizard builder and validator? 

Login as admin to console and setup a new role, say adminWithValidation (see following image) from Configuration -> Administration -> Roles 


Then edit layout of the role by clicking on the just created role and then on layout. Set formClass to


Assign this role to some existing user and then login with that user.

In the following image you can see and example of frontend dates validation in action.


N.B. The custom validator is invoked only if the editing user (logged one) has the role with custom layout, say role adminWithValidation for example.

Enjoy :)

2 VOTIYes, Excellent!Yes, Excellent!
Ti è stato utile questo articolo?
From Tirasa's Blog
The place where we share what we do, learn and discover day by day.
Go to blog >