Announcement Announcement Module
Collapse
No announcement yet.
Frustration with ConstraintViolationException Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Frustration with ConstraintViolationException

    I added integration testing via roo "test integration". However, one of my entities is failing with a ConstraintViolationException (entity created by data on demand object).

    Here's my list of problems:

    1) There's no information about what constraint has failed.
    validation failed for classes [....] during persist time for groups [javax.validation.groups.Default, ]
    That is completely unhelpful.

    2) After poking around I see that I can inspect the ConstraintViolationException object to see more details about the fail. Why does roo not do this automagically!!!! How could someone not want to have more information by default about what constraint failed!!!!

    3) I cannot catch this exception!!! I tried to add this to my entity class to override the persist method injected by AspectJ. The override seems to work. The new line number for the exception is updated for my new code, but the exception is not caught by the try/catch!!!

    This is maddening!! I'm starting to loose hope.


    Code:
        @Transactional
        public void persist() {
            try {
            	if (this.entityManager == null) this.entityManager = entityManager();
            	this.entityManager.persist(this);
            }
            catch (RuntimeException e2) { //-- I've tried various catch clauses, none CAUGHT
            	ConstraintViolationException e = (ConstraintViolationException)e2;
            	Set<ConstraintViolation<?>> s = e.getConstraintViolations();
            	Iterator<ConstraintViolation<?>> i = s.iterator();
            	while (i.hasNext()) {
            		ConstraintViolation<?> cv = i.next();
            		System.out.println("CV Message: === +++ ===: " + cv.getMessage());
            	}
            	throw e;
            }
        }

  • #2
    The errormessage contain no more information because simply the in most cases the message isn't more informative then that, database providers are where spares in telling what is wrong (constraints, erorrs in queries etc).

    I also suggest you readup on transaction management and proxies. Basically the commit happens AFTER the method execution, rendering your try/catch useless. You could put a flush right after the persist but I would advice against that...

    Comment


    • #3
      I have some understanding of how spring does transactions. Roo is using the aspectj mode. Either way though the cut points are before and after the method, so I don't think there's problem catching an exception within a method marked as @Transactional. And in fact it WAS working. The problem is that surefire was simply not saving off my debug println's. I updated the pom.xml and it created output files with my debug statements.

      Code:
      			<plugin>
      				<groupId>org.apache.maven.plugins</groupId>
      				<artifactId>maven-surefire-plugin</artifactId>
      				<version>2.7.2</version>
      				<configuration>
      					<printSummary>false</printSummary>
      					<redirectTestOutputToFile>true</redirectTestOutputToFile>
      					<excludes>
      						<exclude>**/*_Roo_*</exclude>
      					</excludes>
      				</configuration>
      			</plugin>
      Once I was able to see the output I got a much clearer and more informative error.

      Code:
      ConstraintDescriptor: ConstraintDescriptorImpl{annotation=org.hibernate.validator.constraints.Email, payloads=[], hasComposingConstraints=true, isReportAsSingleInvalidConstraint=false, elementType=FIELD, definedOn=DEFINED_LOCALLY, groups=[interface javax.validation.groups.Default], attributes={message={org.hibernate.validator.constraints.Email.message}, payload=[Ljava.lang.Class;@500b2175, groups=[Ljava.lang.Class;@ac1b161}}
      Message:              not a well-formed email address
      Invalid value:        Email_0
      1) Why is DOD not looking at the constraints and generating output that meets the constraint? I understand that roo cannot support all constraints, but why not warn the user during DOD generation that the constraint is unknown and LIKELY to fail validation.

      2) Why is surefire not setup to generate output files by default. Once someone knows what they're doing and wants to save disk space and gain performance they can turn it off?

      3) Why not somewhere somehow inspect the constraint violations automatically during tests generated by roo? This just seems a very fundamental of an issue to me.
      Last edited by robertgass; Jun 25th, 2011, 03:07 PM.

      Comment


      • #4
        Hi Robert,

        I am sure you have seen we already addressed https://jira.springsource.org/browse/ROO-2523 and https://jira.springsource.org/browse/ROO-2524 for you. Please feel free to open another ticket suggesting the appropriate surefire configuration. If there is anything else that does not work as expected please raise it in our Jira so we can fix it.

        Cheers,
        Stefan

        Comment


        • #5
          Thanks Stefan, I saw the update from Alan Stewart. I created this for the surefire configuration change: https://jira.springsource.org/browse/ROO-2526

          I tested updating the standard-project-template.xml in my git clone and it seemed to work ok.

          Comment


          • #6
            I just added the config as suggested by you. Please try out a snapshot build and let me know if it works for you http://static.springsource.org/downl...hp?project=ROO.

            Comment


            • #7
              Looks good to me thanks.

              I've also been playing around with my other complaint. Because this issue is likely to arise during DOD creation of objects, I added this to DataOnDemandMetadata:

              Code:
              	public MethodMetadata getInitMethod() {
              
              ...
              		// Create the method body
              		InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder();
              		String dataField = getDataField().getFieldName().getSymbolName();
              		bodyBuilder.appendFormalLine(dataField + " = " + entityType.getFullyQualifiedTypeName() + "." + findEntriesMethod.getMethodName().getSymbolName() + "(0, " + annotationValues.getQuantity() + ");");
              		bodyBuilder.appendFormalLine("if (data == null) throw new IllegalStateException(\"Find entries implementation for '" + entityType.getSimpleTypeName() + "' illegally returned null\");");
              		bodyBuilder.appendFormalLine("if (!" + dataField + ".isEmpty()) {");
              		bodyBuilder.indent();
              		bodyBuilder.appendFormalLine("return;");
              		bodyBuilder.indentRemove();
              		bodyBuilder.appendFormalLine("}");
              		bodyBuilder.appendFormalLine("");
              		bodyBuilder.appendFormalLine(dataField + " = new java.util.ArrayList<" + getDataField().getFieldType().getParameters().get(0).getNameIncludingTypeParameters() + ">();");
              		bodyBuilder.appendFormalLine("for (int i = 0; i < " + annotationValues.getQuantity() + "; i++) {");
              		bodyBuilder.indent();
              		bodyBuilder.appendFormalLine(entityType.getFullyQualifiedTypeName() + " obj = " + getNewTransientEntityMethod().getMethodName() + "(i);");
              		bodyBuilder.appendFormalLine("try {");
              		bodyBuilder.indent();
              		bodyBuilder.appendFormalLine("obj." + persistMethod.getMethodName().getSymbolName() + "();");
              		bodyBuilder.indentRemove();
              		bodyBuilder.appendFormalLine("}");
              		bodyBuilder.appendFormalLine("catch (javax.validation.ConstraintViolationException e) {");
              		bodyBuilder.indent();
              		bodyBuilder.appendFormalLine("java.util.Set<javax.validation.ConstraintViolation<?>> s = e.getConstraintViolations();");
              		bodyBuilder.appendFormalLine("java.util.Iterator<javax.validation.ConstraintViolation<?>> it = s.iterator();");
              		bodyBuilder.appendFormalLine("String msg = \"\";");
              		bodyBuilder.appendFormalLine("while (it.hasNext()) {");
              		bodyBuilder.indent();
              		bodyBuilder.appendFormalLine("javax.validation.ConstraintViolation<?> cv = it.next();");
              		bodyBuilder.appendFormalLine("msg = msg + \"[\" + cv.getConstraintDescriptor() + \":\" + cv.getMessage() + \"=\" + cv.getInvalidValue() + \"]\";");
              		bodyBuilder.indentRemove();
              		bodyBuilder.appendFormalLine("}");
              		bodyBuilder.appendFormalLine("throw new RuntimeException(msg, e);");
              		bodyBuilder.indentRemove();
              		bodyBuilder.appendFormalLine("}");
              		bodyBuilder.appendFormalLine("obj." + flushMethod.getMethodName().getSymbolName() + "();");
              		bodyBuilder.appendFormalLine(dataField + ".add(obj);");
              		bodyBuilder.indentRemove();
              		bodyBuilder.appendFormalLine("}");
              It simply creates a more informative message for a new exception and chains it to the caught ConstraintViolationException. Unfortunately, I don't see a way to chain a ConstraintViolationException or I would have just thrown a new one of those. Perhaps the proper thing to do would be to create a new exception type, but maybe for test purposes this would work.

              Comment


              • #8
                Can you please attach this to a Jira ticket along with a patch?

                Comment


                • #9
                  https://jira.springsource.org/browse/ROO-2532

                  Patch attached.

                  Comment


                  • #10
                    https://jira.springsource.org/browse/ROO-2532 resolved

                    Comment

                    Working...
                    X