Why Hibernate Validator doesn't care #Valid but #NotNull only? - bean-validation

I'm trying to check constraints look like this.
class Registration {
#Valid
#NotNull
private User user;
}
class User {
#NotBlank
private String name;
#Max(128)
#Min(0)
#PositiveOrZero
private int age;
}
When I try to invoke isValidFor(Registration.class, "user", null) it populates a non empty Set<ConstraintViolation<Registration>> as expected.
But it returns an empty set for isValidFor(Registration.class, "user", <invalid User>).
final Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
final User user = User.newInstanceWithInvalidAge();
Assertions.assertThat(validator.validate(user))
.isNotEmpty(); // succeeds
final BeanDescriptor constraints = validator.getConstraintsForClass(Registration.class);
for (final PropertyDescriptor constrainedProperty
: constraints.getConstrainedProperties()) {
log.debug("constrained property: {}", constrainedProperty);
for (final ConstraintDescriptor<?> constraintDescriptor
: constrainedProperty.getConstraintDescriptors()) {
log.debug("\tconstraint descriptor: {}", constraintDescriptor);
}
}
final Registration registration = Registration.builder().user(user).build();
Assertions.assertThat(validator.validate(registration))
.isNotEmpty(); // succeeds
Assertions.assertThat(validator.validateValue(Registration.class, "user", user))
.isNotEmpty(); // fails
Here comes what it prints.
constrained property: PropertyDescriptorImpl{
propertyName=user,
cascaded=true
}
constraint descriptor: ConstraintDescriptorImpl{
annotation=j.v.c.NotNull,
payloads=[],
hasComposingConstraints=true,
isReportAsSingleInvalidConstraint=false,
elementType=FIELD,
definedOn=DEFINED_LOCALLY,
groups=[interface javax.validation.groups.Default],
attributes={groups=[Ljava.lang.Class;#664a9613,
message={javax.validation.constraints.NotNull.message},
payload=[Ljava.lang.Class;#5118388b},
constraintType=GENERIC,
valueUnwrapping=DEFAULT
}
Why the isValidFor method doesn't care for #Valid?
I'm working on following dependencies.
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.0.1.Final</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.glassfish</groupId>
<artifactId>javax.el</artifactId>
<version>3.0.1-b12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.0.23.Final</version>
<scope>test</scope>
</dependency>

I just found the #Valid is not honored by the validateProperty method.
<T> Set<ConstraintViolation<T>> validateProperty(T object, String propertyName, Class<?>... groups) validates a given field or property of an object. An IllegalArgumentException is thrown when validateProperty() is called and object is null or propertyName is null, empty or invalid or null is passed to the varargs groups parameter. The property name is the JavaBeans property name (as defined by the JavaBeans Introspector class). This method implements the logic described in Validation routine but only to the given property. #Valid is not honored by this method. This method is useful for partial object validation.
I still want to know why.

Related

#AutoConfigureMockMvc fails integration tests with inifnite loop

We had an integration tests such as the one that follows that used to work:
#ActiveProfiles("local")
#WithMockUser("j_unit_user_http_test")
#RunWith(SpringRunner.class)
#SpringBootTest(
classes = { Application.class },
webEnvironment = RANDOM_PORT
)
#Transactional
#Rollback
#AutoConfigureMockMvc()
public abstract class HttpTest {
static {
//reads and sets a dependency related props
PropertiesReader propertiesReader = new PropertiesReader();
propertiesReader.readDependencyProperties().forEach(System::setProperty);
}
#Autowired
private MockMvc mockMvc;
#PersistenceContext
private EntityManager em;
#Test
public void createDashboard() {
// POST is a utility method that wraps "mockMvc.perform(post(url))", I've omitted it here for brevity.
var postResult = POST("/api/dashboards", Map.of("name", "wonderland"));
var newDashboard = extractJson(postResult);
assertTrue(newDashboard.get("id").isInt());
}
}
Among the changes we made the significant ones that seem to be causing the errors are:
Upgrading spring-boot from '2.3.0' to '2.5.6'
Setting the environment properties needed by some of our dependencies in the static void main class of our app:
public class Application {
public static void main(String[] args) {
// reads and sets dependency related props
PropertiesReader propertiesReader = new PropertiesReader();
propertiesReader.readDependencyProperties().forEach(System::setProperty);
}
}
The error we get is:
java.lang.StackOverflowError
at java.base/java.lang.Throwable.getOurStackTrace(Throwable.java:828)
at java.base/java.lang.Throwable.getStackTrace(Throwable.java:820)
at ch.qos.logback.classic.spi.ThrowableProxy.<init>(ThrowableProxy.java:55)
at ch.qos.logback.classic.spi.ThrowableProxy.<init>(ThrowableProxy.java:60) // several frames of this follow
My guess is that the mockMvc is not getting configured correctly, because if I use it without #SpringBootTest and #AutoConfigureMvc, the tests work. Any idea what could be wrong?
The above issue has been fixed.
In the commit when the tests started failing, among other changes, the spring-boot version was changed from 2.3.x to 2.5.x
Turns out in version 2.4 spring-boot removed JUnit 5's vintage engine.
As pointed out in their release notes, ideally, i should migrate the tests to junit 5, but, in the meantime, adding the following in build.gradle helps:
testImplementation("org.junit.vintage:junit-vintage-engine") {
exclude group: "org.hamcrest", module: "hamcrest-core"
}
Equivalent changes in pom.xml would be:
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-core</artifactId>
</exclusion>
</exclusions>
</dependency>

Apache Camel - Multipart File upload

Using Apache-Camel ESB, trying to upload a xlsx file to Spring Rest Web application. Upload fails from apache-camel ESB. But upload works fine from Postman. Shared code snippets below.
Processor Code in Router of Camel looks like
from("file://data/PASInput").process(new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
MultipartEntityBuilder multipartEntityBuilder = MultipartEntityBuilder.create();
multipartEntityBuilder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
String filename = (String) exchange.getIn().getHeader(Exchange.FILE_NAME);
File file = exchange.getIn().getBody(File.class);
multipartEntityBuilder.addPart("file",
new FileBody(file, ContentType.MULTIPART_FORM_DATA, filename));
ByteArrayOutputStream out = new ByteArrayOutputStream();
multipartEntityBuilder.build().writeTo(out);
InputStream inputStream = new ByteArrayInputStream(out.toByteArray());
exchange.getOut().setBody(inputStream);
}
}).to("http://localhost:8080/Pastel/api/convertor/pas/pastel")
.log(LoggingLevel.ERROR, "RESPONSE BODY ${body}").end();
Pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-spring-boot-starter</artifactId>
<version>2.21.0.fuse-000077-redhat-1</version>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-cxf</artifactId>
<version>2.21.0.fuse-000077-redhat-1</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpmime</artifactId>
<version>4.3.1</version>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-http</artifactId>
<version>2.21.0.fuse-000077-redhat-1</version>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-http4</artifactId>
<version>2.17.2</version>
</dependency>
Error
org.apache.camel.http.common.HttpOperationFailedException: HTTP operation failed invoking http://localhost:8080/Pastel/api/convertor/pas/pastel with statusCode: 500
at org.apache.camel.component.http.HttpProducer.populateHttpOperationFailedException(HttpProducer.java:274)
at org.apache.camel.component.http.HttpProducer.process(HttpProducer.java:183)
at org.apache.camel.util.AsyncProcessorConverterHelper$ProcessorToAsyncProcessorBridge.process(AsyncProcessorConverterHelper.java:61)
at org.apache.camel.processor.SendProcessor.process(SendProcessor.java:148)
at org.apache.camel.processor.RedeliveryErrorHandler.process(RedeliveryErrorHandler.java:548)
at org.apache.camel.processor.CamelInternalProcessor.process(CamelInternalProcessor.java:201)
at org.apache.camel.processor.Pipeline.process(Pipeline.java:138)
at org.apache.camel.processor.Pipeline.process(Pipeline.java:101)
at org.apache.camel.processor.CamelInternalProcessor.process(CamelInternalProcessor.java:201)
at org.apache.camel.component.file.GenericFileConsumer.processExchange(GenericFileConsumer.java:452)
at org.apache.camel.component.file.GenericFileConsumer.processBatch(GenericFileConsumer.java:219)
at org.apache.camel.component.file.GenericFileConsumer.poll(GenericFileConsumer.java:183)
at org.apache.camel.impl.ScheduledPollConsumer.doRun(ScheduledPollConsumer.java:174)
at org.apache.camel.impl.ScheduledPollConsumer.run(ScheduledPollConsumer.java:101)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
When we hit the webservice using postman, there are no errors. Able to upload the servers successfully. Spring mvc code,
#RequestMapping(value = "/pas/pastel", method = RequestMethod.POST)
#ResponseBody
public void convertPASToPastel(HttpServletRequest request, HttpServletResponse response,
#RequestParam(value = "file") final MultipartFile pasFile) {
try {
System.out.print("Here");
}
}
You can probably see this error message in your Spring backend log:
org.springframework.web.multipart.MultipartException: Current request is not a multipart request.
You need to set correct ContentType header. Please refer this similar question for solution, if you want to implement it in this way.
But you can get out this mess, if you switch co camel-http4 component (you already have this component in pom.xml). This component contains logic for converting HttpEntity to InputStream. Then you can set HttpEntity directly to exchange body.
Then your route will look something like this:
from("file://data/PASInput").process(new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
MultipartEntityBuilder multipartEntityBuilder = MultipartEntityBuilder.create();
multipartEntityBuilder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
String filename = exchange.getIn().getHeader(Exchange.FILE_NAME, String.class);
File file = exchange.getIn().getBody(File.class);
multipartEntityBuilder.addPart("file",
new FileBody(file, ContentType.MULTIPART_FORM_DATA, filename));
exchange.getOut().setBody(multipartEntityBuilder.build());
}
}).to("http4://localhost:8080/Pastel/api/convertor/pas/pastel")
.log(LoggingLevel.ERROR, "RESPONSE BODY ${body}").end();
And just a note. Never mix component versions, always use for components the same version as Apache Camel version. Otherwise you can see upredictable results. And why you have annotation #ResponseBody in Spring controller, when the method is void? You don`t need that.

HttpMediaTypeNotSupportedException: Content type 'application/json' not supported

Hi I am using Spring MVC. In my controller I am expecting form data
#RequestMapping(value = "/get/data/grid", method = RequestMethod.POST)
public ModelAndView getData(#RequestBody DataGrid dataGrid) { }
But I'm getting this HttpMediaTypeNotSupportedException: Content type 'application/json' not supported error. I am curious it could be because of HsshMap in my formbean object DataGrid This is how the this object looks like:
public class DataGrid implements Serializable{
/**
*
*/
private static final long serialVersionUID = 5979434283059930104L;
private String transactionAction;
private String transactionDate;
private List<GridColumn> columns;
private List<Map<GridColumnName,Object>> rows;
public Grid(){
this.columns = new ArrayList<GridColumn>();
this.rows = new ArrayList<Map<GridColumnName,Object>>();
}
...getter setter here
While calling the Java method write below code in JS:
var xhttp = new XMLHttpRequest();
xhttp.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
I was having exactly same issue except I was returning ResponseEntity<T>. I tried lot of solutions but none of them worked. Added Jackson ASL dependency and <mvc:annotationDriven /> also but nothing solved my problem (Though <mvc:annotationDriven /> was mandatory). Then found below link:
https://spring.io/guides/gs/consuming-rest/
Adding below dependency in pom.xml resolved my issue which was AWESOME !!
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.3.2</version>
</dependency>

CDI WELD #ConversationScoped #Stateful EJB conversation.end() & #Remove SFSB

currently i am trying to remove a ConversationScoped Stateful Session Bean (SFSB). The ConversationScope is managed by the CDI Container and the lifecycle of the SFSB is managed by the EJB Container. Is this correct?
In my Controller i'm trying to end the conversation by calling a method of the SFSB and to call the #Remove annotated method to destroy the SFSB.
The conversation can be end without any problems but i am not able to destroy the SFSB.
A Code example from Weld Reference Guide (WELD Conversation Scope):
#ConversationScoped #Stateful
public class OrderBuilder {
private Order order;
private #Inject Conversation conversation;
private #PersistenceContext(type = EXTENDED) EntityManager em;
#Produces public Order getOrder() {
return order;
}
public Order createOrder() {
order = new Order();
conversation.begin();
return order;
}
public void addLineItem(Product product, int quantity) {
order.add(new LineItem(product, quantity));
}
public void saveOrder(Order order) {
em.persist(order);
conversation.end();
}
#Remove
public void destroy() {}
}
The controller:
#Named
#SessionScoped
public class TestController implements Serializable{
#Inject
private OrderBuilder orderBuilder;
...
public String checkout(Order order){
orderBuilder.saveOrder(order);
orderBuilder.destroy();
return "success";
}
}
After i have called testController.checkout(order), i'am getting this exception:
javax.servlet.ServletException:
java.lang.reflect.InvocationTargetException
javax.faces.webapp.FacesServlet.service(FacesServlet.java:321)
org.jboss.weld.servlet.ConversationPropagationFilter.doFilter(ConversationPropagationFilter.java:67)
root cause
javax.faces.el.EvaluationException:
java.lang.reflect.InvocationTargetException
javax.faces.component.MethodBindingMethodExpressionAdapter.invoke(MethodBindingMethodExpressionAdapter.java:98)
com.sun.faces.application.ActionListenerImpl.processAction(ActionListenerImpl.java:98)
javax.faces.component.UICommand.broadcast(UICommand.java:311)
javax.faces.component.UIViewRoot.broadcastEvents(UIViewRoot.java:781)
javax.faces.component.UIViewRoot.processApplication(UIViewRoot.java:1246)
com.sun.faces.lifecycle.InvokeApplicationPhase.execute(InvokeApplicationPhase.java:77)
com.sun.faces.lifecycle.Phase.doPhase(Phase.java:97)
com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:114)
javax.faces.webapp.FacesServlet.service(FacesServlet.java:308)
org.jboss.weld.servlet.ConversationPropagationFilter.doFilter(ConversationPropagationFilter.java:67)
Any ideas?
THX
You should end the CDI conversation and CDI will call the #Remove method.
Have a look in Weld documentation :
"
Stateful session beans may define a remove method, annotated #Remove, that is used by the application to indicate that an instance should be destroyed. However, for a contextual instance of the bean—an instance under the control of CDI—this method may only be called by the application if the bean has scope #Dependent. For beans with other scopes, the application must let the container destroy the bean.
"
JSF 1.2 or 2.0 does not support expression like methods with parameter
obj.method(parameter)
JSF supoorts only method without parameter like
obj.method()
Seam 2,3 build in supports this kind of expreesions but if you are using only weld (CDI support of seam, core of seam) without other seam jars, you can not have this ability.
But it is possible to give this kind of ability to JSF.
Adding this to jar project you can use methods with parameters. If you are using maven u can use code below or. Download jars manually in lib folder.
<dependency>
<groupId>javax.el</groupId>
<artifactId>el-api</artifactId>
<version>2.2</version>
</dependency>
<dependency>
<groupId>org.glassfish.web</groupId>
<artifactId>el-impl</artifactId>
<version>2.2</version>
</dependency>
Additionally, I tested it with tomcat worked fine, but in jetty some conflicts happen with the other jars. May be it is about my project.
Add this to web xml
<context-param>
<param-name>com.sun.faces.expressionFactory</param-name>
<param-value>com.sun.el.ExpressionFactoryImpl</param-value>
</context-param>

AspectJ AOP not working as expected

I am working on AspectJ AOP implementation on spring MVC application. I have written Aspect java class where I am trying to intercept join points for all the methods of one of the packages say com.xyz.services. But AOP always failed to interecept the methods of that package. Aspect is defined as follows -
#Pointcut("execution(* com.xyz.services..*.*(..))")
public void logBefore() {
}
#Before("logBefore()")
public void logHere(JoinPoint joinPoint) {
System.out.println("In logHere ....");
logger.info("logBefore is running ....");
logger.info("hijacked ::::" + joinPoint.getSignature().getName());
logger.info("joinPoint.getSignature().getDeclaringTypeName() ::::"
+ joinPoint.getSignature().getDeclaringTypeName());
logger.info("joinPoint.getSignature().getModifiers() ::::"
+ joinPoint.getSignature().getModifiers());
logger.info("******************************************************");
}
I have enabled AOP in application-context.xml as follows -
<aop:aspectj-autoproxy proxy-target-class="true">
<aop:include name='loggingAspect' />
</aop:aspectj-autoproxy>
When ever I am calling webservice the methods in the com.xyz.services get called but the Aspect method never get called.
I tried with different pointcuts but the aspect code never did not execute.
#Pointcut("execution(public * com.xyz.services.ManagerServiceImpl.*(..))")
public void logBefore() {
System.out.println("In Logbefore");}
#Pointcut("execution(public * com.xyz.services.*.*(..))")
public void logBefore() {
System.out.println("In Logbefore");
}
I have added cglib dependency on pom.xml to enable cglib based proxies.
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2.2</version>
</dependency>
Can anyone help me out why this aspects are not working as expected?
have you configured correctly the class with the annotation ?
#EnableAspectJAutoProxy(proxyTargetClass = true)

Resources