@Override public void onValidate(final ValidationInterceptorContext ctx) { final Object resource = ctx.getResource(); final Invocable resourceMethod = ctx.getInvocable(); final Object[] args = ctx.getArgs(); final Set<ConstraintViolation<Object>> constraintViolations = new HashSet<>(); final BeanDescriptor beanDescriptor = getConstraintsForClass(resource.getClass()); // Resource validation. if (beanDescriptor.isBeanConstrained()) { constraintViolations.addAll(validate(resource)); } if (resourceMethod != null && configuration.getBootstrapConfiguration().isExecutableValidationEnabled()) { final Method handlingMethod = resourceMethod.getHandlingMethod(); // Resource method validation - input parameters. final MethodDescriptor methodDescriptor = beanDescriptor.getConstraintsForMethod(handlingMethod.getName(), handlingMethod.getParameterTypes()); if (methodDescriptor != null && methodDescriptor.hasConstrainedParameters() && validateOnExecutionHandler.validateMethod(resource.getClass(), resourceMethod.getDefinitionMethod(), resourceMethod.getHandlingMethod())) { constraintViolations.addAll(forExecutables().validateParameters(resource, handlingMethod, args)); } } if (!constraintViolations.isEmpty()) { throw new ConstraintViolationException(constraintViolations); } }
@Override public void validateResult(final Object resource, final Invocable resourceMethod, final Object result) { if (configuration.getBootstrapConfiguration().isExecutableValidationEnabled()) { final Set<ConstraintViolation<Object>> constraintViolations = new HashSet<>(); final Method handlingMethod = resourceMethod.getHandlingMethod(); final BeanDescriptor beanDescriptor = getConstraintsForClass(resource.getClass()); final MethodDescriptor methodDescriptor = beanDescriptor.getConstraintsForMethod(handlingMethod.getName(), handlingMethod.getParameterTypes()); final Method definitionMethod = resourceMethod.getDefinitionMethod(); if (methodDescriptor != null && methodDescriptor.hasConstrainedReturnValue() && validateOnExecutionHandler.validateMethod(resource.getClass(), definitionMethod, handlingMethod)) { constraintViolations.addAll(forExecutables().validateReturnValue(resource, handlingMethod, result)); if (result instanceof Response) { constraintViolations.addAll(forExecutables().validateReturnValue(resource, handlingMethod, ((Response) result).getEntity())); } } if (!constraintViolations.isEmpty()) { throw new ConstraintViolationException(constraintViolations); } } } }
@Test @SpecAssertions({ @SpecAssertion(section = Sections.XML_MAPPING_CONSTRAINTDECLARATIONINXML_METHODLEVELOVERRIDING, id = "a"), @SpecAssertion(section = Sections.XML_MAPPING_CONSTRAINTDECLARATIONINXML_METHODLEVELOVERRIDING, id = "c"), @SpecAssertion(section = Sections.XML_MAPPING_CONSTRAINTDECLARATIONINXML_METHODLEVELOVERRIDING, id = "f") }) public void testXmlMethodConfigurationApplied() throws Exception { MethodDescriptor descriptor = TestUtil.getMethodDescriptor( CustomerRepository.class, "listCustomers" ); assertNotNull( descriptor, "the specified method should be configured in xml" ); assertTrue( descriptor.hasConstrainedReturnValue() ); assertFalse( descriptor.hasConstrainedParameters() ); descriptor = TestUtil.getMethodDescriptor( CustomerRepository.class, "findCustomer", String.class ); assertNotNull( descriptor, "the specified method should be configured in xml" ); assertTrue( descriptor.hasConstrainedReturnValue() ); assertTrue( descriptor.hasConstrainedParameters() ); descriptor = TestUtil.getMethodDescriptor( CustomerRepository.class, "isCustomer", String.class ); assertNotNull( descriptor, "the specified method should be configured in xml" ); assertFalse( descriptor.hasConstrainedReturnValue() ); assertTrue( descriptor.hasConstrainedParameters() ); }
@Test @SpecAssertion(section = Sections.XML_MAPPING_CONSTRAINTDECLARATIONINXML_METHODLEVELOVERRIDING, id = "o") public void testValidaAnnotationIsApplied() throws Exception { MethodDescriptor descriptor = TestUtil.getMethodDescriptor( Cascaded.class, "cascade", String.class ); assertNotNull( descriptor, "the specified method should be configured in xml" ); ReturnValueDescriptor returnValueDescriptor = descriptor.getReturnValueDescriptor(); assertTrue( returnValueDescriptor.isCascaded(), "Cascaded validation should be applied" ); ParameterDescriptor parameterDescriptor = descriptor.getParameterDescriptors().get( 0 ); assertTrue( parameterDescriptor.isCascaded(), "Cascaded validation should be applied" ); } }
@Test @SpecAssertion(section = Sections.CONSTRAINTMETADATA_EXECUTABLEDESCRIPTOR, id = "b") public void testGetParameterDescriptorsForMethod() { MethodDescriptor descriptor = Executables.parameterConstrainedMethod(); assertEquals( descriptor.getParameterDescriptors().size(), 2, "Size of parameter descriptor list doesn't match method parameter count" ); }
@Test @SpecAssertion(section = Sections.XML_MAPPING_CONSTRAINTDECLARATIONINXML_METHODLEVELOVERRIDING, id = "l") public void testIgnoreAnnotationsOnReturnValueParameterAndCrossParameter() { MethodDescriptor descriptor = TestUtil.getMethodDescriptor( IgnoreAnnotations.class, "foobar", String.class, String.class ); CrossParameterDescriptor crossParameterDescriptor = descriptor.getCrossParameterDescriptor(); assertFalse( crossParameterDescriptor.hasConstraints(), "Cross parameter constraints should be ignored." ); ReturnValueDescriptor returnValueDescriptor = descriptor.getReturnValueDescriptor(); assertFalse( returnValueDescriptor.hasConstraints(), "Return value constraints should be ignored." ); ParameterDescriptor parameterDescriptor = descriptor.getParameterDescriptors().get( 0 ); assertFalse( parameterDescriptor.hasConstraints(), "First parameter constraints should be ignored." ); parameterDescriptor = descriptor.getParameterDescriptors().get( 1 ); assertTrue( parameterDescriptor.hasConstraints(), "Second parameter constraints should be applied." ); } }
@Test @SpecAssertion(section = Sections.CONSTRAINTMETADATA_EXECUTABLEDESCRIPTOR, id = "h") public void testMethodDescriptorCanBeRetrievedAlsoIfValidateExecutableIsSetToNONEOnMethodLevel() { MethodDescriptor descriptor = getMethodDescriptor( Item.class, "setName", String.class ); assertNotNull( descriptor ); assertEquals( descriptor.getName(), "setName" ); assertEquals( descriptor.getParameterDescriptors().get( 0 ).getConstraintDescriptors().size(), 1 ); }
@Test @SpecAssertion(section = Sections.CONSTRAINTMETADATA_EXECUTABLEDESCRIPTOR, id = "c") public void testGetCrossParameterDescriptorForMethod() { MethodDescriptor descriptor = Executables.crossParameterConstrainedMethod(); assertNotNull( descriptor.getCrossParameterDescriptor(), "Cross-parameter descriptor should not be null" ); }
@Test @SpecAssertion(section = Sections.CONSTRAINTMETADATA_ELEMENTDESCRIPTOR, id = "d") @SpecAssertion(section = Sections.CONSTRAINTMETADATA_EXECUTABLEDESCRIPTOR, id = "g") public void testFindConstraintsForMethod() { MethodDescriptor parameterConstrainedDescriptor = Executables.parameterConstrainedMethod(); assertTrue( parameterConstrainedDescriptor.findConstraints() .getConstraintDescriptors() .isEmpty(), "Should have no constraints" ); MethodDescriptor returnValueConstrainedDescriptor = Executables.returnValueConstrainedMethod(); assertTrue( returnValueConstrainedDescriptor.findConstraints() .getConstraintDescriptors() .isEmpty(), "Should have no constraints" ); MethodDescriptor crossParameterConstrainedDescriptor = Executables.crossParameterConstrainedMethod(); assertTrue( crossParameterConstrainedDescriptor.findConstraints() .getConstraintDescriptors() .isEmpty(), "Should have no constraints" ); }
@Test @SpecAssertion(section = Sections.CONSTRAINTMETADATA_EXECUTABLEDESCRIPTOR, id = "g") public void testGetConstraintsForMethod() { MethodDescriptor parameterConstrainedDescriptor = Executables.parameterConstrainedMethod(); assertTrue( parameterConstrainedDescriptor.getConstraintDescriptors().isEmpty(), "Should have no constraints" ); MethodDescriptor returnValueConstrainedDescriptor = Executables.returnValueConstrainedMethod(); assertTrue( returnValueConstrainedDescriptor.getConstraintDescriptors().isEmpty(), "Should have no constraints" ); MethodDescriptor crossParameterConstrainedDescriptor = Executables.crossParameterConstrainedMethod(); assertTrue( crossParameterConstrainedDescriptor.getConstraintDescriptors().isEmpty(), "Should have no constraints" ); }
@AroundInvoke public Object invoke(final InvocationContext context) throws Exception { final Method method = context.getMethod(); final Class<?> targetClass = Proxies.classFor(context.getTarget().getClass()); if (!isExecutableValidated(targetClass, method, this::computeIsMethodValidated)) { return context.proceed(); } final MethodDescriptor constraintsForMethod = validator.getConstraintsForClass(targetClass) .getConstraintsForMethod(method.getName(), method.getParameterTypes()); if (!DescriptorManager.isConstrained(constraintsForMethod)) { return context.proceed(); } initExecutableValidator(); if (constraintsForMethod.hasConstrainedParameters()) { final Set<ConstraintViolation<Object>> violations = executableValidator.validateParameters(context.getTarget(), method, context.getParameters()); if (!violations.isEmpty()) { throw new ConstraintViolationException(violations); } } final Object result = context.proceed(); if (constraintsForMethod.hasConstrainedReturnValue()) { final Set<ConstraintViolation<Object>> violations = executableValidator.validateReturnValue(context.getTarget(), method, result); if (!violations.isEmpty()) { throw new ConstraintViolationException(violations); } } return result; }
@Test @SpecAssertion(section = Sections.XML_MAPPING_CONSTRAINTDECLARATIONINXML_METHODLEVELOVERRIDING, id = "i") public void testCascadingOnReturnValueAndParameter() throws Exception { MethodDescriptor descriptor = TestUtil.getMethodDescriptor( CustomerRepository.class, "findByExample", Customer.class ); assertNotNull( descriptor, "the specified method should be configured in xml" ); ReturnValueDescriptor returnValueDescriptor = descriptor.getReturnValueDescriptor(); assertTrue( returnValueDescriptor.isCascaded(), "<valid/> is used to configure cascading" ); List<ParameterDescriptor> parameterDescriptors = descriptor.getParameterDescriptors(); assertTrue( parameterDescriptors.size() == 1 ); ParameterDescriptor parameterDescriptor = parameterDescriptors.get( 0 ); assertTrue( parameterDescriptor.isCascaded(), "<valid/> is used to configure cascading" ); }
@Test @SpecAssertion(section = Sections.CONSTRAINTMETADATA_EXECUTABLEDESCRIPTOR, id = "b") public void testGetParameterDescriptorsForParameterlessMethod() { MethodDescriptor descriptor = Executables.returnValueConstrainedMethod(); assertEquals( descriptor.getParameterDescriptors().size(), 0, "Size of parameter descriptor list doesn't match method parameter count" ); }
@Test @SpecAssertions({ @SpecAssertion(section = Sections.XML_MAPPING_CONSTRAINTDECLARATIONINXML_METHODLEVELOVERRIDING, id = "l"), @SpecAssertion(section = Sections.XML_MAPPING_CONSTRAINTDECLARATIONINXML_METHODLEVELOVERRIDING, id = "m"), @SpecAssertion(section = Sections.XML_MAPPING_CONSTRAINTDECLARATIONINXML_METHODLEVELOVERRIDING, id = "n"), @SpecAssertion(section = Sections.XML_MAPPING_CONSTRAINTDECLARATIONINXML_METHODLEVELOVERRIDING, id = "p") }) public void testIgnoreAnnotationsOnMethodLevel() { MethodDescriptor descriptor = TestUtil.getMethodDescriptor( IgnoreAnnotations.class, "foobar", String.class, String.class ); CrossParameterDescriptor crossParameterDescriptor = descriptor.getCrossParameterDescriptor(); assertFalse( crossParameterDescriptor.hasConstraints(), "Cross parameter constraints should be ignored." ); ReturnValueDescriptor returnValueDescriptor = descriptor.getReturnValueDescriptor(); assertFalse( returnValueDescriptor.hasConstraints(), "Return value constraints should be ignored." ); assertTrue( returnValueDescriptor.getGroupConversions().isEmpty(), "Group conversions should be ignored" ); ParameterDescriptor parameterDescriptor = descriptor.getParameterDescriptors().get( 0 ); assertFalse( parameterDescriptor.hasConstraints(), "First parameter constraints should be ignored." ); assertTrue( parameterDescriptor.getGroupConversions().isEmpty(), "Group conversions should be ignored" ); parameterDescriptor = descriptor.getParameterDescriptors().get( 1 ); assertTrue( parameterDescriptor.hasConstraints(), "Second parameter constraints should be applied." ); assertEquals( parameterDescriptor.getGroupConversions().size(), 2, "All group conversions should be combined" ); } }
@Test @SpecAssertion(section = Sections.CONSTRAINTMETADATA_EXECUTABLEDESCRIPTOR, id = "h") public void testMethodDescriptorCanBeRetrievedAlsoIfValidateExecutableIsSetToNONEOnTypeLevel() { MethodDescriptor descriptor = getMethodDescriptor( OrderLine.class, "setItem", String.class ); assertNotNull( descriptor ); assertEquals( descriptor.getName(), "setItem" ); assertEquals( descriptor.getParameterDescriptors().get( 0 ).getConstraintDescriptors().size(), 1 ); }
@Test @SpecAssertion(section = Sections.CONSTRAINTMETADATA_EXECUTABLEDESCRIPTOR, id = "c") public void testGetCrossParameterDescriptorForMethodWithoutCrossParameterConstraints() { MethodDescriptor descriptor = Executables.returnValueConstrainedMethod(); assertNotNull( descriptor.getCrossParameterDescriptor(), "Cross-parameter descriptor should not be null" ); }
@Test @SpecAssertion(section = Sections.CONSTRAINTMETADATA_ELEMENTDESCRIPTOR, id = "d") @SpecAssertion(section = Sections.CONSTRAINTMETADATA_ELEMENTDESCRIPTOR, id = "g") @SpecAssertion(section = Sections.CONSTRAINTMETADATA_EXECUTABLEDESCRIPTOR, id = "g") public void testFindConstraintsForMethodLookingAt() { MethodDescriptor crossParameterConstrainedDescriptor = Executables.methodOverridingCrossParameterConstrainedMethod(); assertEquals( crossParameterConstrainedDescriptor.findConstraints() .lookingAt( Scope.LOCAL_ELEMENT ) .getConstraintDescriptors() .size(), 0, "Should have no local constraints" ); assertTrue( crossParameterConstrainedDescriptor.findConstraints().lookingAt( Scope.HIERARCHY ) .getConstraintDescriptors() .isEmpty(), "Should have no hierarchy constraints" ); }
@Test @SpecAssertion(section = Sections.CONSTRAINTMETADATA_EXECUTABLEDESCRIPTOR, id = "e") public void testAreParametersConstrainedForCrossParameterConstrainedMethod() { MethodDescriptor descriptor = Executables.crossParameterConstrainedMethod(); assertTrue( descriptor.hasConstrainedParameters(), "Should be constrained on parameters" ); }
@AroundInvoke public Object invoke(final InvocationContext context) throws Exception { final Method method = context.getMethod(); final Class<?> targetClass = Proxies.classFor(context.getTarget().getClass()); if (!isExecutableValidated(targetClass, method, this::computeIsMethodValidated)) { return context.proceed(); } final MethodDescriptor constraintsForMethod = validator.getConstraintsForClass(targetClass) .getConstraintsForMethod(method.getName(), method.getParameterTypes()); if (!DescriptorManager.isConstrained(constraintsForMethod)) { return context.proceed(); } initExecutableValidator(); if (constraintsForMethod.hasConstrainedParameters()) { final Set<ConstraintViolation<Object>> violations = executableValidator.validateParameters(context.getTarget(), method, context.getParameters()); if (!violations.isEmpty()) { throw new ConstraintViolationException(violations); } } final Object result = context.proceed(); if (constraintsForMethod.hasConstrainedReturnValue()) { final Set<ConstraintViolation<Object>> violations = executableValidator.validateReturnValue(context.getTarget(), method, result); if (!violations.isEmpty()) { throw new ConstraintViolationException(violations); } } return result; }
@Test @SpecAssertion(section = Sections.CONSTRAINTMETADATA_EXECUTABLEDESCRIPTOR, id = "f") public void testIsReturnValueConstrainedForCascadedMethod() { MethodDescriptor descriptor = Executables.cascadedReturnValueMethod(); assertTrue( descriptor.hasConstrainedReturnValue(), "Should be constrained on return value" ); }