Matt Wallis
Matt Wallis

Reputation: 923

Inline validation using JSR-303 in GWT, part 1: Validation on TextBox fails. Why?

I want to perform inline validation on the fields in a form, using JSR-303. By inline (aka on-the-fly) validation, I mean that each field is validated as the user works through the form (e.g. on blur or on key press), as opposed to deferring validation until a submit button is pressed. Typically, error messages are displayed near the input field.

I'm following the procedures described in GWT validation dev guide where an example shows JSR-303 constraints being defined for the fields (in this case just one field, name) in a Person class:

// Copied from http://www.gwtproject.org/doc/latest/DevGuideValidation.html
public class Person implements Serializable {
  @NotNull
  @Size(min = 4, message = "Name must be at least 4 characters long.")
  private String name;
} 

The class(es) (just Person in this case) to be validated are specified like this:

// Copied from http://www.gwtproject.org/doc/latest/DevGuideValidation.html
public final class MyValidatorFactory extends AbstractGwtValidatorFactory {
  /**
   * Validator marker for the Validation Sample project. Only the classes and groups listed
   * in the {@link GwtValidation} annotation can be validated.
   */
  @GwtValidation(Person.class)
  public interface GwtValidator extends Validator {
  } 
  @Override
  public AbstractGwtValidator createValidator() {
    return GWT.create(GwtValidator.class);
  }
}

Suppose I have a TextBox on which I want to perform inline validation. I attempt to define constraints like this:

// Example of code that does not work!
import com.google.gwt.user.client.ui.TextBox;
import javax.validation.constraints.Pattern;
public class FooWidget extends TextBox {
  @Pattern(regexp="^[0-9]+$", message="Foo: expected digits.")
  public String getFoo() { 
    return getValue(); // from TextBox
  }
}

with the corresponding definition in MyValidatorFactory:

// Example of code that does not work!
@GwtValidation(FooWidget.class)
public interface GwtValidator extends Validator {
}

However, this does not compile (I'm using GWT 2.7.0), producing the following errors:

   [ERROR] An internal compiler exception occurred
com.google.gwt.dev.jjs.InternalCompilerException: Unexpected error during visit.
    at com.google.gwt.dev.jjs.ast.JVisitor.translateException(JVisitor.java:121)
    at com.google.gwt.dev.jjs.ast.JModVisitor.accept(JModVisitor.java:296)
    at com.google.gwt.dev.jjs.ast.JModVisitor.accept(JModVisitor.java:285)
    at com.google.gwt.dev.jjs.ast.JVisitor.accept(JVisitor.java:128)
    at com.google.gwt.dev.jjs.ast.JCastOperation.traverse(JCastOperation.java:67)
    at com.google.gwt.dev.jjs.ast.JModVisitor.traverse(JModVisitor.java:381)
    at com.google.gwt.dev.jjs.ast.JModVisitor.accept(JModVisitor.java:293)
    at com.google.gwt.dev.jjs.ast.JModVisitor.accept(JModVisitor.java:285)
    at com.google.gwt.dev.jjs.ast.JVisitor.accept(JVisitor.java:128)
    at com.google.gwt.dev.jjs.ast.JDeclarationStatement.traverse(JDeclarationStatement.java:49)
    at com.google.gwt.dev.jjs.ast.JModVisitor$ListContext.traverse(JModVisitor.java:95)
    at com.google.gwt.dev.jjs.ast.JModVisitor.acceptWithInsertRemove(JModVisitor.java:351)
    at com.google.gwt.dev.jjs.ast.JBlock.traverse(JBlock.java:92)
    at com.google.gwt.dev.jjs.ast.JModVisitor.traverse(JModVisitor.java:381)
    at com.google.gwt.dev.jjs.ast.JModVisitor.accept(JModVisitor.java:293)
    at com.google.gwt.dev.jjs.ast.JVisitor.accept(JVisitor.java:149)
    at com.google.gwt.dev.jjs.ast.JVisitor.accept(JVisitor.java:145)
    at com.google.gwt.dev.jjs.ast.JMethodBody.traverse(JMethodBody.java:83)
    at com.google.gwt.dev.jjs.ast.JModVisitor.traverse(JModVisitor.java:381)
    at com.google.gwt.dev.jjs.ast.JModVisitor.accept(JModVisitor.java:293)
    at com.google.gwt.dev.jjs.ast.JModVisitor.accept(JModVisitor.java:285)
    at com.google.gwt.dev.jjs.ast.JMethod.visitChildren(JMethod.java:600)
    at com.google.gwt.dev.jjs.ast.JMethod.traverse(JMethod.java:569)
    at com.google.gwt.dev.jjs.ast.JModVisitor.traverse(JModVisitor.java:381)
    at com.google.gwt.dev.jjs.ast.JModVisitor.accept(JModVisitor.java:293)
    at com.google.gwt.dev.jjs.ast.JModVisitor.accept(JModVisitor.java:285)
    at com.google.gwt.dev.jjs.impl.UnifyAst.mainLoop(UnifyAst.java:1505)
    at com.google.gwt.dev.jjs.impl.UnifyAst.exec(UnifyAst.java:870)
    at com.google.gwt.dev.jjs.JavaToJavaScriptCompiler$Precompiler.unifyJavaAst(JavaToJavaScriptCompiler.java:1305)
    at com.google.gwt.dev.jjs.JavaToJavaScriptCompiler$Precompiler.constructJavaAst(JavaToJavaScriptCompiler.java:1038)
    at com.google.gwt.dev.jjs.JavaToJavaScriptCompiler$Precompiler.precompile(JavaToJavaScriptCompiler.java:954)
    at com.google.gwt.dev.jjs.MonolithicJavaToJavaScriptCompiler.precompile(MonolithicJavaToJavaScriptCompiler.java:303)
    at com.google.gwt.dev.jjs.JavaScriptCompiler.precompile(JavaScriptCompiler.java:38)
    at com.google.gwt.dev.Precompile.precompile(Precompile.java:286)
    at com.google.gwt.dev.Precompile.precompile(Precompile.java:229)
    at com.google.gwt.dev.Precompile.precompile(Precompile.java:145)
    at com.google.gwt.dev.Compiler.run(Compiler.java:206)
    at com.google.gwt.dev.codeserver.Recompiler.doCompile(Recompiler.java:333)
    at com.google.gwt.dev.codeserver.Recompiler.compile(Recompiler.java:161)
    at com.google.gwt.dev.codeserver.Recompiler.recompile(Recompiler.java:119)
    at com.google.gwt.dev.codeserver.Outbox.recompile(Outbox.java:128)
    at com.google.gwt.dev.codeserver.JobRunner.recompile(JobRunner.java:81)
    at com.google.gwt.dev.codeserver.JobRunner.access$100(JobRunner.java:34)
    at com.google.gwt.dev.codeserver.JobRunner$2.run(JobRunner.java:73)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.NoClassDefFoundError: Could not initialize class com.myco.myapp.FooWidget
    at java.lang.Class.forName0(Native Method)
    at java.lang.Class.forName(Class.java:260)
    at com.google.gwt.validation.rebind.BeanHelperCache.createHelper(BeanHelperCache.java:85)
    at com.google.gwt.validation.rebind.ValidatorGenerator.generateGwtSpecificValidator(ValidatorGenerator.java:131)
    at com.google.gwt.validation.rebind.ValidatorGenerator.generate(ValidatorGenerator.java:67)
    at com.google.gwt.core.ext.IncrementalGenerator.generateNonIncrementally(IncrementalGenerator.java:40)
    at com.google.gwt.dev.javac.StandardGeneratorContext.runGeneratorIncrementally(StandardGeneratorContext.java:760)
    at com.google.gwt.dev.cfg.RuleGenerateWith.realize(RuleGenerateWith.java:160)
    at com.google.gwt.dev.shell.StandardRebindOracle$Rebinder.rebind(StandardRebindOracle.java:79)
    at com.google.gwt.dev.shell.StandardRebindOracle.rebind(StandardRebindOracle.java:276)
    at com.google.gwt.dev.shell.StandardRebindOracle.rebind(StandardRebindOracle.java:265)
    at com.google.gwt.dev.DistillerRebindPermutationOracle.getAllPossibleRebindAnswers(DistillerRebindPermutationOracle.java:87)
    at com.google.gwt.dev.jjs.impl.UnifyAst$UnifyVisitor.createStaticRebindExpression(UnifyAst.java:485)
    at com.google.gwt.dev.jjs.impl.UnifyAst$UnifyVisitor.createRebindExpression(UnifyAst.java:443)
    at com.google.gwt.dev.jjs.impl.UnifyAst$UnifyVisitor.handleMagicMethodCall(UnifyAst.java:576)
    at com.google.gwt.dev.jjs.impl.UnifyAst$UnifyVisitor.endVisit(UnifyAst.java:306)
    at com.google.gwt.dev.jjs.ast.JMethodCall.traverse(JMethodCall.java:248)
    at com.google.gwt.dev.jjs.ast.JModVisitor.traverse(JModVisitor.java:381)
    at com.google.gwt.dev.jjs.ast.JModVisitor.accept(JModVisitor.java:293)
    ... 47 more
      [ERROR] at _FooWidgetValidator.java(6): GWT.create(_FooWidgetValidator.class)
         com.google.gwt.dev.jjs.ast.JMethodCall
      [ERROR] at _FooWidgetValidator.java(6): (_FooWidgetValidator) GWT.create(_FooWidgetValidator.class)
         com.google.gwt.dev.jjs.ast.JCastOperation
      [ERROR] at _FooWidgetValidator.java(6): final static _FooWidgetValidator INSTANCE = (_FooWidgetValidator) GWT.create(_FooWidgetValidator.class)
         com.google.gwt.dev.jjs.ast.JDeclarationStatement
      [ERROR] at _FooWidgetValidator.java(5): {
  final static _FooWidgetValidator INSTANCE = (_FooWidgetValidator) GWT.create(_FooWidgetValidator.class);
}
         com.google.gwt.dev.jjs.ast.JBlock
      [ERROR] at _FooWidgetValidator.java(5): {
  final static _FooWidgetValidator INSTANCE = (_FooWidgetValidator) GWT.create(_FooWidgetValidator.class);
}
         com.google.gwt.dev.jjs.ast.JMethodBody
      [ERROR] at _FooWidgetValidator.java(5): private static final void $clinit();

         com.google.gwt.dev.jjs.ast.JMethod
[ERROR] Compiler returned false

What's going on here and why doesn't this work?

The error messages refer to generated source code. I'd like to know how to relate them to my source code. Without understanding exactly what is broken, one resorts to experimental changes in search of a fix :-( .

One of those experimental changes has produced something that seems to work. I've posted another question about that: Inline validation using JSR-303 in GWT, part 2: A good implementation?, so if want to suggest how to do inline validation, maybe look at that question. This question is about understanding why the approach of putting constraints inside my FooWidget does not work.

One further detail: I know that the class to be validated should implement Serializable, as not to do so means it isn't a Java Bean. But there are plenty of examples on the web where that hasn't been done. Does it matter?

Upvotes: 0

Views: 174

Answers (1)

Thomas Broyer
Thomas Broyer

Reputation: 64541

Caused by: java.lang.NoClassDefFoundError: Could not initialize class com.myco.myapp.FooWidget

You have to compile (javac) your classes before calling the GWT Compiler (and put them in its classpath).

(note: the GWT validation generator should probably try to load the class from GWT's internal TypeOracle rather than from the classloader; also note that GWT-Validation support is almost unmaintained – someone recently volunteered to maintain it, but hasn't contributed much yet)

Upvotes: 1

Related Questions