Validation of input beans is now performed automatically

git-svn-id: https://svn.d4science.research-infrastructures.eu/gcube/trunk/data-catalogue/grsf-publisher-ws@133247 82a268e6-3cf1-43bd-a215-b396298e98cf
This commit is contained in:
Costantino Perciante 2016-10-15 20:34:57 +00:00
parent f128ca18ab
commit 9bb23c991f
11 changed files with 143 additions and 88 deletions

View File

@ -3,9 +3,6 @@
<wb-resource deploy-path="/" source-path="/target/m2e-wtp/web-resources"/>
<wb-resource deploy-path="/" source-path="/src/main/webapp" tag="defaultRootSource"/>
<wb-resource deploy-path="/WEB-INF/classes" source-path="/src/main/java"/>
<dependent-module archiveName="ckan-util-library-2.0.0-SNAPSHOT.jar" deploy-path="/WEB-INF/lib" handle="module:/resource/ckan-util-library/ckan-util-library">
<dependency-type>uses</dependency-type>
</dependent-module>
<property name="context-root" value="grsf-publisher-ws"/>
<property name="java-output-path" value="/grsf-publisher-ws/target/classes"/>
</wb-module>

View File

@ -0,0 +1,22 @@
package org.gcube.data_catalogue.grsf_publish_ws.ex;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;
@Provider
/**
* Exception thrown when @Valid fail
* @author Costantino Perciante at ISTI-CNR
*/
public class ApplicationException implements ExceptionMapper<Exception> {
public Response toResponse(Exception e) {
return Response
.status(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode())
.type(MediaType.APPLICATION_JSON)
.entity(e.getMessage())
.build();
}
}

View File

@ -0,0 +1,29 @@
package org.gcube.data_catalogue.grsf_publish_ws.ex;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;
@Provider
/**
* Exception thrown on fail
* @author Costantino Perciante at ISTI-CNR
*/
public class ValidationException implements ExceptionMapper<javax.validation.ValidationException> {
@Override
public Response toResponse(javax.validation.ValidationException e) {
final StringBuilder strBuilder = new StringBuilder();
for (ConstraintViolation<?> cv : ((ConstraintViolationException) e).getConstraintViolations()) {
strBuilder.append(cv.getPropertyPath().toString() + " " + cv.getMessage());
}
return Response
.status(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode())
.type(MediaType.APPLICATION_JSON)
.entity(strBuilder.toString())
.build();
}
}

View File

@ -4,6 +4,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
@ -52,11 +53,13 @@ public class Common {
@JsonProperty("database_sources")
@NotNull(message="database_source cannot be null")
@Size(min=1, message="database_source cannot be empty")
@Valid
private List<DatabaseSource> databaseSources;
@JsonProperty("source_of_information")
@NotNull(message="source_of_information cannot be null")
@Size(min=1, message="source_of_information cannot be empty")
@Valid
private List<Resource> sourceOfInformation;
@JsonProperty("data_owner")

View File

@ -7,6 +7,8 @@ import java.util.Map;
import javax.servlet.ServletContext;
import javax.validation.Valid;
import javax.validation.ValidationException;
import javax.validation.constraints.NotNull;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
@ -81,7 +83,8 @@ public class GrsfPublisherFisheryService {
@Path("publish-product")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response publishFishery(FisheryRecord record){
public Response publishFishery(
@NotNull(message="record cannot be null") @Valid FisheryRecord record) throws ValidationException{
// retrieve context and username
Caller caller = AuthorizationProvider.instance.get();
@ -95,18 +98,18 @@ public class GrsfPublisherFisheryService {
Status status = Status.INTERNAL_SERVER_ERROR;
String id = "";
// validate the bean
logger.debug("Start validating bean...");
Response responseAfterValidation = HelperMethods.validateBeanAndResources(record);
if(responseAfterValidation == null)
logger.debug("Bean validation successful");
else{
logger.warn("Bean validation failed");
return responseAfterValidation;
}
// // validate the bean
// logger.debug("Start validating bean...");
// Response responseAfterValidation = HelperMethods.validateBeanAndResources(record);
//
// if(responseAfterValidation == null)
// logger.debug("Bean validation successful");
// else{
//
// logger.warn("Bean validation failed");
// return responseAfterValidation;
//
// }
try{
@ -217,7 +220,7 @@ public class GrsfPublisherFisheryService {
if(HelperMethods.existsLicenseId(record.getLicense()))
license = record.getLicense();
else throw new Exception("Please check the license id!");
long version = record.getVersion() == null ? 1 : record.getVersion();
// create the product
@ -263,12 +266,12 @@ public class GrsfPublisherFisheryService {
return Response.status(status).entity(responseBean).build();
}
@DELETE
@Path("delete-product")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response deleteFishery(@Valid DeleteProductBean recordToDelete){
public Response deleteFishery(@NotNull(message="missing input value") @Valid DeleteProductBean recordToDelete) throws ValidationException{
// retrieve context and username
Caller caller = AuthorizationProvider.instance.get();
@ -278,7 +281,7 @@ public class GrsfPublisherFisheryService {
ResponseCreationBean responseBean = new ResponseCreationBean();
Status status = Status.INTERNAL_SERVER_ERROR;
// check it is a stock ...
// check it is a fishery ...
logger.info("Received call to delete product with id " + recordToDelete.getId() + ", checking if it is a fishery");
try{
@ -302,7 +305,7 @@ public class GrsfPublisherFisheryService {
}
// get extras and check there is the field Assessment distribution area that is mandatory for stock
// get extras and check there is the field Fishery Name that is mandatory for fishery
if(fisheryInCkan.getExtrasAsHashMap().containsKey("Fishery Name")){
logger.warn("Ok, this is a fishery, removing it");

View File

@ -7,6 +7,8 @@ import java.util.Map;
import javax.servlet.ServletContext;
import javax.validation.Valid;
import javax.validation.ValidationException;
import javax.validation.constraints.NotNull;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
@ -81,7 +83,8 @@ public class GrsfPublisherStockService {
@Path("publish-product")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response publishStock(StockRecord record){
public Response publishStock(
@NotNull(message="record cannot be null") @Valid StockRecord record) throws ValidationException{
// retrieve context and username
Caller caller = AuthorizationProvider.instance.get();
@ -95,18 +98,18 @@ public class GrsfPublisherStockService {
Status status = Status.INTERNAL_SERVER_ERROR;
String id = "";
// validate the bean
logger.debug("Start validating bean...");
Response responseAfterValidation = HelperMethods.validateBeanAndResources(record);
if(responseAfterValidation == null)
logger.debug("Bean validation successful");
else{
logger.warn("Bean validation failed");
return responseAfterValidation;
}
// // validate the bean
// logger.debug("Start validating bean...");
// Response responseAfterValidation = HelperMethods.validateBeanAndResources(record);
//
// if(responseAfterValidation == null)
// logger.debug("Bean validation successful");
// else{
//
// logger.warn("Bean validation failed");
// return responseAfterValidation;
//
// }
try{
@ -259,7 +262,7 @@ public class GrsfPublisherStockService {
@Path("delete-product")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response deleteStock(@Valid DeleteProductBean recordToDelete){
public Response deleteStock(@NotNull(message="missing input value") @Valid DeleteProductBean recordToDelete) throws ValidationException{
// retrieve context and username
Caller caller = AuthorizationProvider.instance.get();

View File

@ -8,14 +8,8 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.servlet.ServletContext;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
import javax.ws.rs.core.Response;
import org.gcube.common.scope.api.ScopeProvider;
import org.gcube.data_catalogue.grsf_publish_ws.custom_annotations.CustomField;
@ -24,7 +18,6 @@ import org.gcube.data_catalogue.grsf_publish_ws.custom_annotations.Tag;
import org.gcube.data_catalogue.grsf_publish_ws.json.input.Common;
import org.gcube.data_catalogue.grsf_publish_ws.json.input.DatabaseSource;
import org.gcube.data_catalogue.grsf_publish_ws.json.input.Resource;
import org.gcube.data_catalogue.grsf_publish_ws.json.output.ResponseCreationBean;
import org.gcube.data_catalogue.grsf_publish_ws.utils.groups.Source;
import org.gcube.data_catalogue.grsf_publish_ws.utils.groups.Status;
import org.gcube.datacatalogue.ckanutillibrary.DataCatalogue;
@ -113,7 +106,7 @@ public abstract class HelperMethods {
}
}
while((current = current.getSuperclass())!=null);
// now parse also the Database Sources field
List<DatabaseSource> sources = record.getDatabaseSources();
for (DatabaseSource databaseSource : sources) {
@ -359,49 +352,49 @@ public abstract class HelperMethods {
}
/**
* Validate a record along the database_sources and the source_of_information
* @param record
* @return
*/
public static Response validateBeanAndResources(Common record){
ResponseCreationBean responseBean = new ResponseCreationBean();
javax.ws.rs.core.Response.Status status = javax.ws.rs.core.Response.Status.BAD_REQUEST;
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
Validator validator = factory.getValidator();
Set<ConstraintViolation<Common>> violations = validator.validate(record);
for (ConstraintViolation<Common> constraintViolation : violations) {
logger.warn("Violation is about " + constraintViolation.getPropertyPath() + ", message error is " + constraintViolation.getMessage());
responseBean.setError(constraintViolation.getMessage());
return Response.status(status).entity(responseBean).build();
}
// check database_sources and source_of_information (they are not null nor empty at this point)
List<DatabaseSource> databaseSources = record.getDatabaseSources();
for (DatabaseSource databaseSource : databaseSources) {
Set<ConstraintViolation<DatabaseSource>> violationsDatabaseSourcesBean = validator.validate(databaseSource);
for (ConstraintViolation<DatabaseSource> constraintViolation : violationsDatabaseSourcesBean) {
logger.warn("Violation is about " + constraintViolation.getPropertyPath() + ", message error is " + constraintViolation.getMessage());
responseBean.setError(constraintViolation.getMessage());
return Response.status(status).entity(responseBean).build();
}
}
List<Resource> sourcesOfInformation = record.getSourceOfInformation();
for (Resource sourceOfinformation : sourcesOfInformation) {
Set<ConstraintViolation<Resource>> violationsSourceOfinformationsBean = validator.validate(sourceOfinformation);
for (ConstraintViolation<Resource> constraintViolation : violationsSourceOfinformationsBean) {
logger.warn("Violation is about " + constraintViolation.getPropertyPath() + ", message error is " + constraintViolation.getMessage());
responseBean.setError(constraintViolation.getMessage());
return Response.status(status).entity(responseBean).build();
}
}
return null;
}
// /**
// * Validate a record along the database_sources and the source_of_information
// * @param record
// * @return
// */
// public static Response validateBeanAndResources(Common record){
//
// ResponseCreationBean responseBean = new ResponseCreationBean();
// javax.ws.rs.core.Response.Status status = javax.ws.rs.core.Response.Status.BAD_REQUEST;
//
// ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
// Validator validator = factory.getValidator();
//
//
// Set<ConstraintViolation<Common>> violations = validator.validate(record);
// for (ConstraintViolation<Common> constraintViolation : violations) {
// logger.warn("Violation is about " + constraintViolation.getPropertyPath() + ", message error is " + constraintViolation.getMessage());
// responseBean.setError(constraintViolation.getMessage());
// return Response.status(status).entity(responseBean).build();
// }
//
// // check database_sources and source_of_information (they are not null nor empty at this point)
// List<DatabaseSource> databaseSources = record.getDatabaseSources();
// for (DatabaseSource databaseSource : databaseSources) {
// Set<ConstraintViolation<DatabaseSource>> violationsDatabaseSourcesBean = validator.validate(databaseSource);
// for (ConstraintViolation<DatabaseSource> constraintViolation : violationsDatabaseSourcesBean) {
// logger.warn("Violation is about " + constraintViolation.getPropertyPath() + ", message error is " + constraintViolation.getMessage());
// responseBean.setError(constraintViolation.getMessage());
// return Response.status(status).entity(responseBean).build();
// }
// }
//
// List<Resource> sourcesOfInformation = record.getSourceOfInformation();
// for (Resource sourceOfinformation : sourcesOfInformation) {
// Set<ConstraintViolation<Resource>> violationsSourceOfinformationsBean = validator.validate(sourceOfinformation);
// for (ConstraintViolation<Resource> constraintViolation : violationsSourceOfinformationsBean) {
// logger.warn("Violation is about " + constraintViolation.getPropertyPath() + ", message error is " + constraintViolation.getMessage());
// responseBean.setError(constraintViolation.getMessage());
// return Response.status(status).entity(responseBean).build();
// }
// }
// return null;
// }
/**
* Retrieve the ResourceBean given the record (extract resources from Database Sources and Source of Information)

View File

@ -4,6 +4,7 @@
<version>1.0.0-SNAPSHOT</version>
<description>Data Catalogue Service</description>
<local-persistence location='target' />
<exclude>/rest/</exclude>
<exclude>/rest/stock/hello</exclude>
<exclude>/rest/fishery/hello</exclude>
</application>

View File

@ -12,6 +12,10 @@
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>org.gcube.data_catalogue.grsf_publish_ws.services</param-value>
</init-param>
<init-param>
<param-name>jersey.config.beanValidation.enableOutputValidationErrorEntity.server</param-name>
<param-value>true</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>

View File

@ -1,5 +1,5 @@
<html>
<body>
<h2>The social networking web service is up and running!</h2>
<h2>The GRSF publisher web service is up and running!</h2>
</body>
</html>

View File

@ -24,7 +24,7 @@ public class JJerseyTest extends JerseyTest{
//@Override
protected Application configure() {
forceSet(TestProperties.CONTAINER_PORT, "0");
return new ResourceConfig(GrsfPublisherFisheryService.class, GrsfPublisherStockService.class);
return new ResourceConfig(GrsfPublisherFisheryService.class, GrsfPublisherStockService.class).property("jersey.config.beanValidation.enableOutputValidationErrorEntity.server", true);
}
//@Test