Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 20 additions & 13 deletions ms-common-impl/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<artifactId>ms-common-impl</artifactId>
<name>Common Microservice Implementation</name>
<properties>
<resteasy.version>3.1.4.Final</resteasy.version>
<resteasy.version>4.0.0.Beta2</resteasy.version>
<reuseForks>false</reuseForks>
<slf4j.version>1.7.16</slf4j.version>
</properties>
Expand Down Expand Up @@ -37,10 +37,6 @@
<groupId>net.jcip</groupId>
<artifactId>jcip-annotations</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
Expand Down Expand Up @@ -74,16 +70,27 @@
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.bval</groupId>
<artifactId>bval-jsr</artifactId>
<groupId>org.eclipse</groupId>
<artifactId>yasson</artifactId>
<version>1.0.1</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.glassfish</groupId>
<artifactId>javax.el</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.glassfish</groupId>
<artifactId>javax.json</artifactId>
<version>1.1.2</version>
<scope>runtime</scope>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.0.8.Final</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>net.trajano.commons</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ public class VertxMicroserviceEngine implements
* Sets the system properties and sets up the logger. {@inheritDoc}
*/
@Override
public Object[] bootstrap() {
public Class<?>[] bootstrap() {

System.setProperty("vertx.logger-delegate-factory-class-name", "io.vertx.core.logging.SLF4JLogDelegateFactory");

Expand All @@ -76,7 +76,7 @@ public Object[] bootstrap() {
System.setProperty("logging.config", logbackFile.getAbsolutePath());
}

return new Object[] {
return new Class<?>[] {
VertxConfig.class,
VertxMicroserviceEngine.class
};
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
package net.trajano.ms.vertx.beans;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.Valid;
import javax.validation.Validator;
import javax.validation.constraints.NotNull;
import javax.ws.rs.BadRequestException;
import javax.ws.rs.FormParam;
import javax.ws.rs.HeaderParam;
import javax.ws.rs.InternalServerErrorException;
import javax.ws.rs.PathParam;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;

import org.jboss.resteasy.spi.HttpRequest;
import org.jboss.resteasy.spi.validation.GeneralValidator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import net.trajano.ms.core.ConstraintViolationResponse;
import net.trajano.ms.core.ErrorResponses;

/**
* This performs validation checks on the request message. If the request
* message does not pass validation it will throw a bad request exception.
*
* @author Archimedes Trajano
*/
@Component
public class BeanValidator implements
GeneralValidator {

/**
* Logger.
*/
private static final Logger LOG = LoggerFactory.getLogger(BeanValidator.class);

@Autowired
private Validator validator;

@Override
public void checkViolations(final HttpRequest request) {

// does nothing

}

/**
* Obtain the name from JAX-RS annotations. If the parameter is not annotated is
* uses "body" as the name.
*
* @param parameter
* parameter
* @return parameter name
*/
private String getParameterName(final Parameter parameter) {

final String name;
if (parameter.getAnnotation(FormParam.class) != null) {
name = "form." + parameter.getAnnotation(FormParam.class).value();
} else if (parameter.getAnnotation(QueryParam.class) != null) {
name = "query." + parameter.getAnnotation(QueryParam.class).value();
} else if (parameter.getAnnotation(PathParam.class) != null) {
name = "path." + parameter.getAnnotation(PathParam.class).value();
} else if (parameter.getAnnotation(HeaderParam.class) != null) {
name = "header." + parameter.getAnnotation(HeaderParam.class).value();
} else if (parameter.isAnnotationPresent(Context.class)) {
name = parameter.getType().getName();
} else {
name = "body";
}
return name;
}

@Override
public boolean isMethodValidatable(final Method method) {

return true;
}

@Override
public boolean isValidatable(final Class<?> clazz) {

return true;
}

@Override
public void validate(final HttpRequest request,
final Object object,
final Class<?>... groups) {

LOG.debug("validate {} {}", request, object);

}

/**
* {@inheritDoc}. {@link Context} annotated parameters are skipped unless the
* {@link Valid} annotation is present.
*/
@Override
public void validateAllParameters(final HttpRequest request,
final Object object,
final Method method,
final Object[] parameterValues,
final Class<?>... groups) {

LOG.debug("validateAllParameters {} {} {}", request, object, parameterValues);

final Map<String, Set<ConstraintViolation<Object>>> violationMap = new LinkedHashMap<>();
boolean hasViolation = false;
for (int i = 0; i < method.getParameterCount(); ++i) {

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MINOR Reduce the total number of break and continue statements in this loop to use at most one. rule


final Parameter parameter = method.getParameters()[i];
if (parameter.isAnnotationPresent(Context.class) && !parameter.isAnnotationPresent(Valid.class)) {
continue;
}

final Object value = parameterValues[i];

final String name = getParameterName(parameter);

final Set<ConstraintViolation<Object>> violations;
if (value == null) {
if (parameter.isAnnotationPresent(NotNull.class)) {
throw ErrorResponses.invalidRequest("missing value for " + name);
} else {
continue;
}
} else {
violations = validator.validate(value);
}
if (violations.isEmpty()) {
continue;
}

violationMap.put(name, violations);
hasViolation = true;
}
if (hasViolation) {
throw new BadRequestException(Response.status(Status.BAD_REQUEST).entity(new ConstraintViolationResponse(violationMap)).build());
}

}

@Override
public void validateReturnValue(final HttpRequest request,
final Object object,
final Method method,
final Object returnValue,
final Class<?>... groups) {

LOG.debug("validateReturnValue {} {} {}", request, object, returnValue);

if (returnValue == null) {
if (method.isAnnotationPresent(NotNull.class)) {
throw ErrorResponses.internalServerError("unexpected null result");
} else {
return;
}
}

final Set<ConstraintViolation<Object>> errors = validator.validate(returnValue);

if (!errors.isEmpty()) {
throw new InternalServerErrorException("result object validation failed", new ConstraintViolationException(errors));
}

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package net.trajano.ms.vertx.beans;

import javax.validation.Validation;
import javax.validation.Validator;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ValidationProvider {

@Bean
public Validator beanValidationValidator() {

return Validation.buildDefaultValidatorFactory().getValidator();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package net.trajano.ms.vertx.jaxrs;

import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.annotation.PostConstruct;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.UriInfo;
import javax.ws.rs.ext.ExceptionMapper;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;

import net.trajano.ms.Microservice;

public abstract class AbstractJsonExceptionMapper<T extends Throwable> implements
ExceptionMapper<T> {

private static final Logger LOG = LoggerFactory.getLogger(Microservice.class);

/**
* Supported media types.
*/
private static final Set<MediaType> SUPPORTED_MEDIA_TYPES = new HashSet<>(Arrays.asList(
MediaType.APPLICATION_JSON_TYPE,
MediaType.APPLICATION_XML_TYPE,
MediaType.TEXT_XML_TYPE,
MediaType.TEXT_PLAIN_TYPE,
MediaType.TEXT_HTML_TYPE));

@Context
private HttpHeaders headers;

@Value("${microservice.show_stack_trace:#{null}}")
protected Boolean showStackTrace;

@Context
protected UriInfo uriInfo;

/**
* Determines the appropriate media type based on what is requested. If wildcard
* use JSON.
*
* @return media type appropriate for request
*/
protected MediaType getAppropriateMediaType() {

final List<MediaType> acceptableMediaTypes = headers.getAcceptableMediaTypes();
for (final MediaType mediaType : acceptableMediaTypes) {
if (mediaType.equals(MediaType.WILDCARD_TYPE)) {
return MediaType.APPLICATION_JSON_TYPE;
} else if (SUPPORTED_MEDIA_TYPES.contains(mediaType)) {
return mediaType;
}
}
return MediaType.APPLICATION_JSON_TYPE;

}

/**
* This sets the context data so the mapper can be unit tested.
*/
public void setContextData(final HttpHeaders headers,
final UriInfo uriInfo,
final boolean showStackTrace) {

this.headers = headers;
this.uriInfo = uriInfo;
this.showStackTrace = showStackTrace;

}

/**
* If the show request URI or show stack trace are not defined, it will default
* to whether the current logger is on debug mode or not.
*/
@PostConstruct
public void setDebugFlags() {

if (showStackTrace == null) {
showStackTrace = LOG.isDebugEnabled();
LOG.debug("stack trace enabled if this is shown");
}
}

}
Loading