package ameba.message.error; import ameba.core.Application; import ameba.core.Requests; import ameba.exception.UnprocessableEntityException; import ameba.util.ClassUtils; import ameba.util.Result; import com.google.common.collect.Lists; import com.google.common.hash.Hashing; import org.apache.commons.lang3.StringUtils; import org.glassfish.jersey.server.internal.process.MappableException; import org.glassfish.jersey.server.spi.ResponseErrorMapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.annotation.Priority; import javax.inject.Inject; import javax.inject.Singleton; import javax.ws.rs.Priorities; import javax.ws.rs.container.ResourceInfo; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import javax.ws.rs.ext.ExceptionMapper; import java.util.List; /** * <p>DefaultExceptionMapper class.</p> * * @author icode * @since 13-8-17 下午2:00 * */ @Singleton @Priority(Priorities.USER) public class DefaultExceptionMapper implements ExceptionMapper<Throwable>, ResponseErrorMapper { /** * Constant <code>BEFORE_EXCEPTION_KEY="DefaultExceptionMapper.class.getName() "{trunked}</code> */ public static final String BEFORE_EXCEPTION_KEY = DefaultExceptionMapper.class.getName() + ".BEFORE_EXCEPTION"; private static final Logger logger = LoggerFactory.getLogger(DefaultExceptionMapper.class); @Inject private Application.Mode mode; @Context private ResourceInfo resourceInfo; /** * <p>parseHttpStatus.</p> * * @param exception a {@link java.lang.Throwable} object. * @return a int. */ protected int parseHttpStatus(Throwable exception) { return ErrorMessage.parseHttpStatus(exception); } /** * <p>parseMessage.</p> * * @param exception a {@link java.lang.Throwable} object. * @param status a int. * @return a {@link java.lang.String} object. */ protected String parseMessage(Throwable exception, int status) { return ErrorMessage.parseMessage(status); } /** * <p>parseDescription.</p> * * @param exception a {@link java.lang.Throwable} object. * @param status a int. * @return a {@link java.lang.String} object. */ protected String parseDescription(Throwable exception, int status) { if (exception instanceof UnprocessableEntityException && StringUtils.isNotBlank(exception.getMessage())) { return exception.getMessage(); } return ErrorMessage.parseDescription(status); } /** * <p>parseErrors.</p> * * @param exception a {@link java.lang.Throwable} object. * @param status a int. * @return a {@link java.util.List} object. */ protected List<Result.Error> parseErrors(Throwable exception, int status) { List<Result.Error> errors = Lists.newArrayList(); boolean isDev = mode.isDev(); if (resourceInfo != null && (status == 500 || status == 400)) { Class clazz = resourceInfo.getResourceClass(); if (clazz != null) { errors.add(new Result.Error( Hashing.murmur3_32().hashUnencodedChars(exception.getClass().getName()).toString(), exception.getMessage(), null, isDev ? ClassUtils.toString(clazz, resourceInfo.getResourceMethod()) : null )); } } if (isDev) { errors.addAll(ErrorMessage.parseErrors(exception, status)); } return errors; } /** {@inheritDoc} */ @Override public Response toResponse(Throwable exception) { int status = parseHttpStatus(exception); ErrorMessage message = new ErrorMessage(); if (exception instanceof MappableException && exception.getCause() != null) { exception = exception.getCause(); } message.setCode(Hashing.murmur3_32().hashUnencodedChars(exception.getClass().getName()).toString()); message.setStatus(status); message.setThrowable(exception); message.setMessage(parseMessage(exception, status)); message.setDescription(parseDescription(exception, status)); message.setErrors(parseErrors(exception, status)); MediaType type = ExceptionMapperUtils.getResponseType(status); if (status == 500) { String uri = ""; if (Requests.getRequest() != null) { uri = " > " + Requests.getUriInfo().getRequestUri(); } logger.error(message.getMessage() + uri, exception); } else if (status == 404) { Requests.setProperty(BEFORE_EXCEPTION_KEY, exception); } return Response.status(status) .type(type) .entity(message).build(); } }