br.com.modoagil.asr.rest.support.GenericWebService.java Source code

Java tutorial

Introduction

Here is the source code for br.com.modoagil.asr.rest.support.GenericWebService.java

Source

/*
 *     Copyright 2014 Modo ?gil
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package br.com.modoagil.asr.rest.support;

import static org.springframework.web.bind.annotation.RequestMethod.DELETE;
import static org.springframework.web.bind.annotation.RequestMethod.GET;
import static org.springframework.web.bind.annotation.RequestMethod.POST;
import static org.springframework.web.bind.annotation.RequestMethod.PUT;

import java.util.List;
import java.util.Set;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.Valid;

import org.apache.commons.lang3.exception.ExceptionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import br.com.modoagil.asr.model.support.AbstractEntity;
import br.com.modoagil.asr.repository.support.GenericRepository;

/**
 * Servios comuns a todos os EndPoints REST, contendo tambm mtodos utilitrios de construo de mensagens de resposta
 *
 * @created 18/12/2014
 * @author Bruno Csar Ribeiro e Silva - <a href="mailto:bruno@brunocesar.com">bruno@brunocesar.com</a>
 */
public abstract class GenericWebService<E extends AbstractEntity, R extends GenericRepository<E, Long>> {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    protected static final String APPLICATION_JSON = MediaType.APPLICATION_JSON_VALUE;

    @Autowired
    private MessageSource messageSource;

    /**
     * Lista todos os objetos contantes na base de dados
     *
     * @return {@link Response} resposta do processamento
     */
    @ResponseBody
    @RequestMapping(value = WebServicesURL.URL_LIST, method = { GET, POST }, produces = APPLICATION_JSON)
    public final Response<E> list() {
        Response<E> response;
        this.getLogger().debug("listando objetos");
        try {
            final List<E> dataList = this.getRepository().findAll();
            final Integer dataListSize = dataList.size();
            final String message = dataListSize > 0 ? String.format(ResponseMessages.LIST_MESSAGE, dataListSize)
                    : ResponseMessages.NOTFOUND_LIST_MESSAGE;
            response = new ResponseBuilder<E>().success(true).data(dataList).message(message).status(HttpStatus.OK)
                    .build();
            this.getLogger().debug(message);
        } catch (final Exception e) {
            final String message = ExceptionUtils.getRootCauseMessage(e);
            response = new ResponseBuilder<E>().success(false).message(message).status(HttpStatus.BAD_REQUEST)
                    .build();
            this.getLogger().error("erro ao listar objetos " + message, e);
        }
        return response;
    }

    /**
     * Consulta objeto pelo id
     *
     * @param id
     *            identificado do objeto a ser consultado
     * @return {@link Response} resposta do processamento
     */
    @ResponseBody
    @RequestMapping(value = WebServicesURL.URL_FIND, method = { GET, POST }, produces = APPLICATION_JSON)
    public final Response<E> find(@PathVariable("id") final Long id) {
        Response<E> response;
        this.getLogger().debug("consultando objeto de id " + id);
        try {
            final E entity = this.getRepository().findOne(id);
            String message = String.format(ResponseMessages.FIND_MESSAGE, id);
            Boolean success = true;
            HttpStatus status = HttpStatus.OK;
            if (entity == null) {
                message = String.format(ResponseMessages.NOTFOUND_MESSAGE, id);
                success = false;
                status = HttpStatus.NOT_FOUND;
            }
            response = new ResponseBuilder<E>().success(success).data(entity).message(message).status(status)
                    .build();
            this.getLogger().debug(message);
        } catch (final Exception e) {
            final String message = ExceptionUtils.getRootCauseMessage(e);
            response = new ResponseBuilder<E>().success(false).message(message).status(HttpStatus.BAD_REQUEST)
                    .build();
            this.getLogger().error("problema ao consultar objeto: " + message, e);
        }
        return response;
    }

    /**
     * Persisti um objeto
     *
     * @param entity
     *            objeto a ser persistido
     * @return {@link Response} resposta do processamento
     */
    @ResponseBody
    @RequestMapping(value = WebServicesURL.URL_CREATE, method = POST, consumes = APPLICATION_JSON, produces = APPLICATION_JSON)
    public final Response<E> create(@Valid @RequestBody final E entity) {
        Response<E> response;
        this.getLogger().debug("criando objeto");
        try {
            final E persistedEntity = this.getRepository().save(entity);
            this.afterCreate(persistedEntity);
            this.getLogger().debug("objeto " + persistedEntity.toString() + " criado com sucesso");
            response = new ResponseBuilder<E>().success(true).data(persistedEntity)
                    .message(ResponseMessages.CREATE_MESSAGE).status(HttpStatus.OK).build();
        } catch (final Exception e) {
            final String message = ExceptionUtils.getRootCauseMessage(e);
            response = this.handlingCatchedExceptions(e, message);
            this.getLogger().error("problema ao criar objeto " + entity.toString() + ": " + message, e);
        }
        return response;
    }

    /**
     * Atualiza o estado de um objeto persistido
     *
     * @param entity
     *            entidade a ser atualizada
     * @return {@link Response} resposta do processamento
     */
    @ResponseBody
    @RequestMapping(value = WebServicesURL.URL_UPDATE, method = PUT, consumes = APPLICATION_JSON, produces = APPLICATION_JSON)
    public final Response<E> update(@Valid @RequestBody final E entity) {
        Response<E> response;
        this.getLogger().debug("atualizando objeto " + entity.toString());
        final E oldEntity = this.getRepository().findOne(entity.getId());
        if (oldEntity != null) {
            try {
                final E persistedEntity = this.getRepository().save(entity);
                this.afterUpdate(persistedEntity);
                response = new ResponseBuilder<E>().success(true).data(persistedEntity)
                        .message(String.format(ResponseMessages.UPDATE_MESSAGE, entity.getId()))
                        .status(HttpStatus.OK).build();
                this.getLogger().debug("objeto " + persistedEntity.toString() + " atualizado com sucesso");
            } catch (final Exception e) {
                final String message = ExceptionUtils.getRootCauseMessage(e);
                response = this.handlingCatchedExceptions(e, message);
                this.getLogger().error("problema ao atualizar objeto " + entity.toString() + ": " + message, e);
            }
        } else {
            response = new ResponseBuilder<E>().success(false).data(entity)
                    .message(String.format(ResponseMessages.NOTFOUND_UPDATE_MESSAGE, entity.getId()))
                    .status(HttpStatus.NOT_FOUND).build();
        }
        return response;
    }

    /**
     * Exclui um objeto persistido
     *
     * @param id
     *            id da entidade a ser removida
     * @return {@link Response} resposta do processamento
     */
    @ResponseBody
    @RequestMapping(value = WebServicesURL.URL_DELETE, method = DELETE, produces = APPLICATION_JSON)
    public final Response<E> delete(@PathVariable("id") final Long id) {
        Response<E> response;
        this.getLogger().debug("excluindo objeto de id " + id);
        final E entity = this.getRepository().findOne(id);
        if (entity != null) {
            try {
                this.beforeDelete(entity);
                this.getRepository().delete(entity);
                response = new ResponseBuilder<E>().success(true).data(entity)
                        .message(String.format(ResponseMessages.DELETE_MESSAGE, id)).status(HttpStatus.OK).build();
                this.getLogger().debug("objeto " + entity.toString() + " excluido com sucesso");
            } catch (final Exception e) {
                final String message = ExceptionUtils.getRootCauseMessage(e);
                response = new ResponseBuilder<E>().success(false).message(message).status(HttpStatus.BAD_REQUEST)
                        .build();
                this.getLogger().error("problema ao excluir objeto " + entity.toString() + ": " + message, e);
            }
        } else {
            response = new ResponseBuilder<E>().success(false)
                    .message(String.format(ResponseMessages.NOTFOUND_DELETE_MESSAGE, id))
                    .status(HttpStatus.NOT_FOUND).build();
        }
        return response;
    }

    /**
     * Executa alteraes na entidade de domnio depois da criao do registro
     *
     * @param entity
     *            entidade do modelo de domnio
     */
    protected void afterCreate(final E entity) {
    }

    /**
     * Executa alteraes na entidade de domnio antes da criao do registro
     *
     * @param entity
     *            entidade do modelo de domnio
     */
    protected void beforeCreate(final E entity) {
    }

    /**
     * Executa alteraes na entidade de domnio depois da atualizao do registro
     *
     * @param entity
     *            entidade do modelo de domnio
     */
    protected void afterUpdate(final E entity) {
    }

    /**
     * Executa alteraes na entidade de domnio antes da atualizao do registro
     *
     * @param entity
     *            entidade do modelo de domnio
     */
    protected void beforeUpdate(final E entity) {
    }

    /**
     * Executa alteraes na entidade de domnio depois da remoo do registro
     *
     * @param entity
     *            entidade do modelo de domnio
     */
    protected void afterDelete(final E entity) {
    }

    /**
     * Executa alteraes na entidade de domnio antes da remoo do registro
     *
     * @param entity
     *            entidade do modelo de domnio
     */
    protected void beforeDelete(final E entity) {
    }

    /**
     * Recupera o repositrio de acesso a dados aos objetos de domnio
     *
     * @return {@code R extends GenericRepository<E, Long>}
     */
    protected abstract R getRepository();

    /**
     * Logger para uso tambm nas sub-classes
     *
     * @return {@link Logger}
     */
    protected Logger getLogger() {
        return this.logger;
    }

    /**
     * Trata a exceo, para gerao da resposta, captura pelos end points
     *
     * @param ex
     *            {@link Exception}
     * @return resposta ao cliente
     */
    protected Response<E> handlingCatchedExceptions(final Exception ex, final String message) {
        Response<E> response;
        if (ex instanceof ConstraintViolationException) {
            response = this.buildResponseForConstraintViolationException((ConstraintViolationException) ex);
        } else {
            response = new ResponseBuilder<E>().success(false).message(message).status(HttpStatus.BAD_REQUEST)
                    .build();
        }
        return response;
    }

    /**
     * Manipula excees para status HTTP {@code 5xx}, excees do servidor
     *
     * @param ex
     *            {@link ConstraintViolationException}
     * @return resposta ao cliente
     */
    private Response<E> buildResponseForConstraintViolationException(final ConstraintViolationException ex) {
        this.logger.info("handleConstraintViolationException - Catching: " + ex.getClass().getSimpleName(), ex);
        final StringBuilder messageResponse = new StringBuilder();
        final Set<ConstraintViolation<?>> violations = ex.getConstraintViolations();
        Integer violationsSize = violations.size();
        for (final ConstraintViolation<?> violation : violations) {
            messageResponse.append(this.messageSource.getMessage(violation.getMessage(), null, null));
            if (violationsSize > 1) {
                messageResponse.append(" | ");
            }
            violationsSize--;
        }
        return new ResponseBuilder<E>().success(false).message(messageResponse.toString())
                .status(HttpStatus.BAD_REQUEST).build();
    }

}