Java tutorial
/** * ============================================================================= * * ORCID (R) Open Source * http://orcid.org * * Copyright (c) 2012-2014 ORCID, Inc. * Licensed under an MIT-Style License (MIT) * http://orcid.org/open-source-license * * This copyright and license information (including a link to the full license) * shall be included in its entirety in all copies or substantial portion of * the software. * * ============================================================================= */ package org.orcid.api.common.jaxb; import java.util.HashMap; import java.util.Locale; import java.util.Map; import javax.annotation.Resource; import javax.persistence.NoResultException; import javax.ws.rs.Consumes; import javax.ws.rs.Produces; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import javax.ws.rs.core.UriInfo; import javax.ws.rs.core.Response.Status; import javax.ws.rs.ext.ExceptionMapper; import javax.ws.rs.ext.Provider; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang3.text.StrSubstitutor; import org.apache.commons.lang3.tuple.ImmutablePair; import org.apache.commons.lang3.tuple.Pair; import org.orcid.core.api.OrcidApiConstants; import org.orcid.core.exception.ActivityIdentifierValidationException; import org.orcid.core.exception.ActivityTitleValidationException; import org.orcid.core.exception.ApplicationException; import org.orcid.core.exception.DuplicatedGroupIdRecordException; import org.orcid.core.exception.GroupIdRecordNotFoundException; import org.orcid.core.exception.InvalidPutCodeException; import org.orcid.core.exception.MismatchedPutCodeException; import org.orcid.core.exception.OrcidApiException; import org.orcid.core.exception.OrcidBadRequestException; import org.orcid.core.exception.OrcidClientNotFoundException; import org.orcid.core.exception.OrcidDeprecatedException; import org.orcid.core.exception.OrcidDuplicatedActivityException; import org.orcid.core.exception.OrcidDuplicatedElementException; import org.orcid.core.exception.OrcidForbiddenException; import org.orcid.core.exception.OrcidInvalidScopeException; import org.orcid.core.exception.OrcidNotFoundException; import org.orcid.core.exception.OrcidNotificationAlreadyReadException; import org.orcid.core.exception.OrcidNotificationException; import org.orcid.core.exception.OrcidNotificationNotFoundException; import org.orcid.core.exception.OrcidUnauthorizedException; import org.orcid.core.exception.OrcidValidationException; import org.orcid.core.exception.OrcidVisibilityException; import org.orcid.core.exception.OrcidWebhookNotFoundException; import org.orcid.core.exception.OtherNameNotFoundException; import org.orcid.core.exception.PutCodeRequiredException; import org.orcid.core.exception.WrongSourceException; import org.orcid.core.locale.LocaleManager; import org.orcid.core.manager.OrcidSecurityManager; import org.orcid.core.security.aop.LockedException; import org.orcid.core.version.ApiSection; import org.orcid.core.web.filters.ApiVersionFilter; import org.orcid.jaxb.model.error.OrcidError; import org.orcid.jaxb.model.message.DeprecatedDate; import org.orcid.jaxb.model.message.ErrorDesc; import org.orcid.jaxb.model.message.Orcid; import org.orcid.jaxb.model.message.OrcidDeprecated; import org.orcid.jaxb.model.message.OrcidMessage; import org.orcid.jaxb.model.message.PrimaryRecord; import org.orcid.pojo.ajaxForm.PojoUtil; import org.orcid.utils.DateUtils; import org.orcid.utils.OrcidStringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.MessageSource; import org.springframework.security.core.AuthenticationException; import org.springframework.security.oauth2.common.exceptions.OAuth2Exception; import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestAttributes; import org.springframework.web.context.request.RequestContextHolder; import com.sun.jersey.api.NotFoundException; /** * orcid-api - Nov 8, 2011 - OrcidExceptionMapper * * @author Declan Newman (declan) */ @Provider @Consumes(value = { OrcidApiConstants.VND_ORCID_JSON, OrcidApiConstants.VND_ORCID_XML, OrcidApiConstants.ORCID_JSON, OrcidApiConstants.ORCID_XML, MediaType.APPLICATION_XML, MediaType.WILDCARD, MediaType.APPLICATION_JSON }) @Produces(value = { OrcidApiConstants.VND_ORCID_JSON, OrcidApiConstants.VND_ORCID_XML, OrcidApiConstants.ORCID_JSON, OrcidApiConstants.ORCID_XML, MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) @Component public class OrcidExceptionMapper implements ExceptionMapper<Throwable> { private static final Logger LOGGER = LoggerFactory.getLogger(OrcidExceptionMapper.class); private static final String LOCATION_HEADER = "location"; @Context private UriInfo uriInfo; @Resource private MessageSource messageSource; @Resource private LocaleManager localeManager; @Resource private OrcidSecurityManager securityManager; private static Map<Class<? extends Throwable>, Pair<Response.Status, Integer>> HTTP_STATUS_AND_ERROR_CODE_BY_THROWABLE_TYPE = new HashMap<>(); { // 301 HTTP_STATUS_AND_ERROR_CODE_BY_THROWABLE_TYPE.put(OrcidDeprecatedException.class, new ImmutablePair<>(Response.Status.MOVED_PERMANENTLY, 9007)); // 400 HTTP_STATUS_AND_ERROR_CODE_BY_THROWABLE_TYPE.put(IllegalArgumentException.class, new ImmutablePair<>(Response.Status.BAD_REQUEST, 9006)); HTTP_STATUS_AND_ERROR_CODE_BY_THROWABLE_TYPE.put(OrcidBadRequestException.class, new ImmutablePair<>(Response.Status.BAD_REQUEST, 9012)); HTTP_STATUS_AND_ERROR_CODE_BY_THROWABLE_TYPE.put(MismatchedPutCodeException.class, new ImmutablePair<>(Response.Status.BAD_REQUEST, 9019)); HTTP_STATUS_AND_ERROR_CODE_BY_THROWABLE_TYPE.put(InvalidPutCodeException.class, new ImmutablePair<>(Response.Status.BAD_REQUEST, 9024)); HTTP_STATUS_AND_ERROR_CODE_BY_THROWABLE_TYPE.put(OrcidValidationException.class, new ImmutablePair<>(Response.Status.BAD_REQUEST, 9020)); HTTP_STATUS_AND_ERROR_CODE_BY_THROWABLE_TYPE.put(ActivityTitleValidationException.class, new ImmutablePair<>(Response.Status.BAD_REQUEST, 9022)); HTTP_STATUS_AND_ERROR_CODE_BY_THROWABLE_TYPE.put(ActivityIdentifierValidationException.class, new ImmutablePair<>(Response.Status.BAD_REQUEST, 9023)); HTTP_STATUS_AND_ERROR_CODE_BY_THROWABLE_TYPE.put(GroupIdRecordNotFoundException.class, new ImmutablePair<>(Response.Status.BAD_REQUEST, 9026)); HTTP_STATUS_AND_ERROR_CODE_BY_THROWABLE_TYPE.put(OtherNameNotFoundException.class, new ImmutablePair<>(Response.Status.BAD_REQUEST, 9033)); // 401 HTTP_STATUS_AND_ERROR_CODE_BY_THROWABLE_TYPE.put(AuthenticationException.class, new ImmutablePair<>(Response.Status.UNAUTHORIZED, 9002)); HTTP_STATUS_AND_ERROR_CODE_BY_THROWABLE_TYPE.put(OAuth2Exception.class, new ImmutablePair<>(Response.Status.UNAUTHORIZED, 9003)); HTTP_STATUS_AND_ERROR_CODE_BY_THROWABLE_TYPE.put(OrcidUnauthorizedException.class, new ImmutablePair<>(Response.Status.UNAUTHORIZED, 9017)); HTTP_STATUS_AND_ERROR_CODE_BY_THROWABLE_TYPE.put(OrcidInvalidScopeException.class, new ImmutablePair<>(Response.Status.UNAUTHORIZED, 9015)); // 403 HTTP_STATUS_AND_ERROR_CODE_BY_THROWABLE_TYPE.put(SecurityException.class, new ImmutablePair<>(Response.Status.FORBIDDEN, 9004)); HTTP_STATUS_AND_ERROR_CODE_BY_THROWABLE_TYPE.put(IllegalStateException.class, new ImmutablePair<>(Response.Status.FORBIDDEN, 9005)); HTTP_STATUS_AND_ERROR_CODE_BY_THROWABLE_TYPE.put(OrcidNotificationAlreadyReadException.class, new ImmutablePair<>(Response.Status.FORBIDDEN, 9009)); HTTP_STATUS_AND_ERROR_CODE_BY_THROWABLE_TYPE.put(WrongSourceException.class, new ImmutablePair<>(Response.Status.FORBIDDEN, 9010)); HTTP_STATUS_AND_ERROR_CODE_BY_THROWABLE_TYPE.put(OrcidForbiddenException.class, new ImmutablePair<>(Response.Status.FORBIDDEN, 9014)); HTTP_STATUS_AND_ERROR_CODE_BY_THROWABLE_TYPE.put(OrcidVisibilityException.class, new ImmutablePair<>(Response.Status.FORBIDDEN, 9013)); // 404 HTTP_STATUS_AND_ERROR_CODE_BY_THROWABLE_TYPE.put(OrcidNotFoundException.class, new ImmutablePair<>(Response.Status.NOT_FOUND, 9011)); HTTP_STATUS_AND_ERROR_CODE_BY_THROWABLE_TYPE.put(NoResultException.class, new ImmutablePair<>(Response.Status.NOT_FOUND, 9016)); HTTP_STATUS_AND_ERROR_CODE_BY_THROWABLE_TYPE.put(OrcidClientNotFoundException.class, new ImmutablePair<>(Response.Status.NOT_FOUND, 9027)); HTTP_STATUS_AND_ERROR_CODE_BY_THROWABLE_TYPE.put(OrcidWebhookNotFoundException.class, new ImmutablePair<>(Response.Status.NOT_FOUND, 9028)); HTTP_STATUS_AND_ERROR_CODE_BY_THROWABLE_TYPE.put(OrcidNotificationNotFoundException.class, new ImmutablePair<>(Response.Status.NOT_FOUND, 9029)); // 409 HTTP_STATUS_AND_ERROR_CODE_BY_THROWABLE_TYPE.put(LockedException.class, new ImmutablePair<>(Response.Status.CONFLICT, 9018)); HTTP_STATUS_AND_ERROR_CODE_BY_THROWABLE_TYPE.put(OrcidDuplicatedActivityException.class, new ImmutablePair<>(Response.Status.CONFLICT, 9021)); HTTP_STATUS_AND_ERROR_CODE_BY_THROWABLE_TYPE.put(DuplicatedGroupIdRecordException.class, new ImmutablePair<>(Response.Status.CONFLICT, 9025)); HTTP_STATUS_AND_ERROR_CODE_BY_THROWABLE_TYPE.put(OrcidDuplicatedElementException.class, new ImmutablePair<>(Response.Status.CONFLICT, 9030)); HTTP_STATUS_AND_ERROR_CODE_BY_THROWABLE_TYPE.put(PutCodeRequiredException.class, new ImmutablePair<>(Response.Status.CONFLICT, 9031)); HTTP_STATUS_AND_ERROR_CODE_BY_THROWABLE_TYPE.put(OrcidNotificationException.class, new ImmutablePair<>(Response.Status.CONFLICT, 9032)); } @Override public Response toResponse(Throwable t) { // Whatever exception has been caught, make sure we log it. String clientId = securityManager.getClientIdFromAPIRequest(); if (PojoUtil.isEmpty(clientId)) { LOGGER.error("An exception has occured, no client id info provided", t); } else { if (t instanceof NotFoundException) { StringBuffer temp = new StringBuffer("An exception has occured processing request from client ") .append(clientId).append(". ").append(t.getMessage()); LOGGER.error(temp.toString()); } else { LOGGER.error("An exception has occured processing request from client " + clientId, t); } } switch (getApiSection()) { case NOTIFICATIONS: return newStyleErrorResponse(t); case V2: return newStyleErrorResponse(t); default: return legacyErrorResponse(t); } } private Response legacyErrorResponse(Throwable t) { if (OrcidApiException.class.isAssignableFrom(t.getClass())) { return ((OrcidApiException) t).getResponse(); } else if (OrcidValidationException.class.isAssignableFrom(t.getClass())) { OrcidMessage entity = getLegacyOrcidEntity("Bad Request: ", t); return Response.status(Response.Status.BAD_REQUEST).entity(entity).build(); } else if (WebApplicationException.class.isAssignableFrom(t.getClass())) { OrcidMessage entity = getLegacy500OrcidEntity(t); WebApplicationException webException = (WebApplicationException) t; return Response.status(webException.getResponse().getStatus()).entity(entity).build(); } else if (AuthenticationException.class.isAssignableFrom(t.getClass())) { OrcidMessage entity = getLegacyOrcidEntity("Authentication problem : ", t); return Response.status(Response.Status.UNAUTHORIZED).entity(entity).build(); } else if (OAuth2Exception.class.isAssignableFrom(t.getClass())) { OrcidMessage entity = getLegacyOrcidEntity("OAuth2 problem : ", t); return Response.status(Response.Status.UNAUTHORIZED).entity(entity).build(); } else if (OrcidInvalidScopeException.class.isAssignableFrom(t.getClass())) { OrcidMessage entity = getLegacyOrcidEntity("OAuth2 problem : ", t); return Response.status(Response.Status.UNAUTHORIZED).entity(entity).build(); } else if (SecurityException.class.isAssignableFrom(t.getClass())) { OrcidMessage entity = getLegacyOrcidEntity("Security problem : ", t); return Response.status(Response.Status.FORBIDDEN).entity(entity).build(); } else if (IllegalStateException.class.isAssignableFrom(t.getClass())) { OrcidMessage entity = getLegacyOrcidEntity("Illegal state : ", t); return Response.status(Response.Status.FORBIDDEN).entity(entity).build(); } else if (IllegalArgumentException.class.isAssignableFrom(t.getClass())) { OrcidMessage entity = getLegacyOrcidEntity("Bad Request : ", t); return Response.status(Response.Status.BAD_REQUEST).entity(entity).build(); } else if (OrcidDeprecatedException.class.isAssignableFrom(t.getClass())) { OrcidDeprecatedException exception = (OrcidDeprecatedException) t; OrcidDeprecated depreciatedError = new OrcidDeprecated(); Map<String, String> params = exception.getParams(); String location = null; if (params != null) { if (params.containsKey(OrcidDeprecatedException.ORCID)) { PrimaryRecord pr = new PrimaryRecord(); pr.setOrcid(new Orcid(params.get(OrcidDeprecatedException.ORCID))); depreciatedError.setPrimaryRecord(pr); String deprecatedOrcid = OrcidStringUtils.getOrcidNumber(uriInfo.getAbsolutePath().toString()); String primaryOrcid = OrcidStringUtils .getOrcidNumber(params.get(OrcidDeprecatedException.ORCID)); location = uriInfo.getAbsolutePath().toString().replace(deprecatedOrcid, primaryOrcid); } if (params.containsKey(OrcidDeprecatedException.DEPRECATED_DATE)) { DeprecatedDate dd = new DeprecatedDate(); String dateString = params.get(OrcidDeprecatedException.DEPRECATED_DATE); dd.setValue(DateUtils.convertToXMLGregorianCalendar(dateString, false)); depreciatedError.setDate(dd); } } Response response = null; if (location != null) { response = Response.status(Response.Status.MOVED_PERMANENTLY).header(LOCATION_HEADER, location) .entity(depreciatedError).build(); } else { response = Response.status(Response.Status.MOVED_PERMANENTLY).entity(depreciatedError).build(); } return response; } else if (LockedException.class.isAssignableFrom(t.getClass())) { OrcidMessage entity = getLegacyOrcidEntity("Account locked : ", t); return Response.status(Response.Status.CONFLICT).entity(entity).build(); } else if (NoResultException.class.isAssignableFrom(t.getClass())) { OrcidMessage entity = getLegacyOrcidEntity("Not found : ", t); return Response.status(Response.Status.NOT_FOUND).entity(entity).build(); } else { OrcidMessage entity = getLegacy500OrcidEntity(t); return Response.status(getHttpStatusAndErrorCode(t).getKey()).entity(entity).build(); } } private OrcidMessage getLegacy500OrcidEntity(Throwable e) { OrcidMessage entity = new OrcidMessage(); entity.setMessageVersion(OrcidMessage.DEFAULT_VERSION); entity.setErrorDesc(new ErrorDesc(StringUtils.isNotBlank(e.getMessage()) ? e.getMessage() : messageSource.getMessage("apiError.unknown.exception", null, localeManager.getLocale()))); return entity; } private OrcidMessage getLegacyOrcidEntity(String prefix, Throwable e) { OrcidMessage entity = new OrcidMessage(); entity.setMessageVersion(OrcidMessage.DEFAULT_VERSION); if (e != null && !PojoUtil.isEmpty(e.getMessage())) entity.setErrorDesc(new ErrorDesc(prefix + e.getMessage())); else entity.setErrorDesc(new ErrorDesc(prefix)); return entity; } private Response newStyleErrorResponse(Throwable t) { if (WebApplicationException.class.isAssignableFrom(t.getClass())) { return getOrcidErrorResponse((WebApplicationException) t); } else { return getOrcidErrorResponse(t); } } private Response getOrcidErrorResponse(Throwable t) { Pair<Response.Status, Integer> pair = getHttpStatusAndErrorCode(t); return getOrcidErrorResponse(pair.getRight(), pair.getLeft(), t); } private Response getOrcidErrorResponse(WebApplicationException e) { int status = e.getResponse().getStatus(); return getOrcidErrorResponse(9001, status, e); } private Response getOrcidErrorResponse(int errorCode, Response.Status status, Throwable t) { return getOrcidErrorResponse(errorCode, status.getStatusCode(), t); } private Response getOrcidErrorResponse(int errorCode, int status, Throwable t) { OrcidError orcidError = getOrcidError(errorCode, status, t); if (OrcidDeprecatedException.class.isAssignableFrom(t.getClass())) { OrcidDeprecatedException exception = (OrcidDeprecatedException) t; Map<String, String> params = exception.getParams(); String location = null; if (params != null) { if (params.containsKey(OrcidDeprecatedException.ORCID)) { String deprecatedOrcid = OrcidStringUtils.getOrcidNumber(uriInfo.getAbsolutePath().toString()); String primaryOrcid = OrcidStringUtils .getOrcidNumber(params.get(OrcidDeprecatedException.ORCID)); location = uriInfo.getAbsolutePath().toString().replace(deprecatedOrcid, primaryOrcid); } } Response response = null; if (location != null) { response = Response.status(status).header(LOCATION_HEADER, location).entity(orcidError).build(); } else { response = Response.status(status).entity(orcidError).build(); } return response; } return Response.status(status).entity(orcidError).build(); } private OrcidError getOrcidError(int errorCode, int status, Throwable t) { Locale locale = localeManager.getLocale(); OrcidError orcidError = new OrcidError(); orcidError.setResponseCode(status); orcidError.setErrorCode(errorCode); orcidError.setMoreInfo(messageSource.getMessage("apiError." + errorCode + ".moreInfo", null, locale)); Map<String, String> params = null; if (t instanceof ApplicationException) { params = ((ApplicationException) t).getParams(); } // Returns an empty message if the key is not found String devMessage = messageSource.getMessage("apiError." + errorCode + ".developerMessage", null, "", locale); // Assign message from the exception if ("".equals(devMessage)) { devMessage = t.getClass().getCanonicalName(); Throwable cause = t.getCause(); String exceptionMessage = t.getLocalizedMessage(); if (exceptionMessage != null) { devMessage += ": " + exceptionMessage; } if (cause != null) { String causeMessage = cause.getLocalizedMessage(); if (causeMessage != null) { devMessage += " (" + causeMessage + ")"; } } orcidError.setDeveloperMessage(devMessage); } else { orcidError.setDeveloperMessage(resolveMessage(devMessage, params)); } orcidError.setUserMessage(resolveMessage( messageSource.getMessage("apiError." + errorCode + ".userMessage", null, locale), params)); return orcidError; } private String resolveMessage(String errorMessg, Map<String, String> params) { if (params == null) { return errorMessg; } StrSubstitutor sub = new StrSubstitutor(params); return sub.replace(errorMessg); } private ApiSection getApiSection() { RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes(); ApiSection apiSection = (ApiSection) requestAttributes .getAttribute(ApiVersionFilter.API_SECTION_REQUEST_ATTRIBUTE_NAME, RequestAttributes.SCOPE_REQUEST); return apiSection != null ? apiSection : ApiSection.V1; } private Pair<Status, Integer> getHttpStatusAndErrorCode(Throwable t) { Pair<Response.Status, Integer> pair = HTTP_STATUS_AND_ERROR_CODE_BY_THROWABLE_TYPE.get(t.getClass()); if (pair != null) { return pair; } // Try super class pair = HTTP_STATUS_AND_ERROR_CODE_BY_THROWABLE_TYPE.get(t.getClass().getSuperclass()); return pair != null ? pair : new ImmutablePair<>(Response.Status.INTERNAL_SERVER_ERROR, 9008); } }