/* * Copyright 2014-2016 Jakub Jirutka <jakub@jirutka.cz>. * * 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 cz.jirutka.spring.exhandler.handlers; import cz.jirutka.spring.exhandler.interpolators.MessageInterpolator; import cz.jirutka.spring.exhandler.interpolators.MessageInterpolatorAware; import cz.jirutka.spring.exhandler.interpolators.NoOpMessageInterpolator; import cz.jirutka.spring.exhandler.interpolators.SpelMessageInterpolator; import cz.jirutka.spring.exhandler.messages.ErrorMessage; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.MessageSource; import org.springframework.context.MessageSourceAware; import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.http.HttpStatus; import org.springframework.util.Assert; import javax.servlet.http.HttpServletRequest; import java.net.URI; import java.util.HashMap; import java.util.Locale; import java.util.Map; /** * {@link RestExceptionHandler} that produces {@link ErrorMessage}. * * @param <E> Type of the handled exception. */ public class ErrorMessageRestExceptionHandler<E extends Exception> extends AbstractRestExceptionHandler<E, ErrorMessage> implements MessageSourceAware, MessageInterpolatorAware { private static final Logger LOG = LoggerFactory.getLogger(ErrorMessageRestExceptionHandler.class); protected static final String DEFAULT_PREFIX = "default", TYPE_KEY = "type", TITLE_KEY = "title", DETAIL_KEY = "detail", INSTANCE_KEY = "instance"; private MessageSource messageSource; private MessageInterpolator interpolator = new SpelMessageInterpolator(); /** * @param exceptionClass Type of the handled exceptions; it's used as a prefix of key to * resolve messages (via MessageSource). * @param status HTTP status that will be sent to client. */ public ErrorMessageRestExceptionHandler(Class<E> exceptionClass, HttpStatus status) { super(exceptionClass, status); } /** * @see AbstractRestExceptionHandler#AbstractRestExceptionHandler(HttpStatus) AbstractRestExceptionHandler */ protected ErrorMessageRestExceptionHandler(HttpStatus status) { super(status); } public ErrorMessage createBody(E ex, HttpServletRequest req) { ErrorMessage m = new ErrorMessage(); m.setType(URI.create(resolveMessage(TYPE_KEY, ex, req))); m.setTitle(resolveMessage(TITLE_KEY, ex, req)); m.setStatus(getStatus()); m.setDetail(resolveMessage(DETAIL_KEY, ex, req)); m.setInstance(URI.create(resolveMessage(INSTANCE_KEY, ex, req))); return m; } protected String resolveMessage(String key, E exception, HttpServletRequest request) { String template = getMessage(key, LocaleContextHolder.getLocale()); Map<String, Object> vars = new HashMap<>(2); vars.put("ex", exception); vars.put("req", request); return interpolateMessage(template, vars); } protected String interpolateMessage(String messageTemplate, Map<String, Object> variables) { LOG.trace("Interpolating message '{}' with variables: {}", messageTemplate, variables); return interpolator.interpolate(messageTemplate, variables); } protected String getMessage(String key, Locale locale) { String prefix = getExceptionClass().getName(); String message = messageSource.getMessage(prefix + "." + key, null, null, locale); if (message == null) { message = messageSource.getMessage(DEFAULT_PREFIX + "." + key, null, null, locale); } if (message == null) { message = ""; LOG.debug("No message found for {}.{}, nor {}.{}", prefix, key, DEFAULT_PREFIX, key); } return message; } ////// Accessors ////// public void setMessageSource(MessageSource messageSource) { Assert.notNull(messageSource, "messageSource must not be null"); this.messageSource = messageSource; } public void setMessageInterpolator(MessageInterpolator interpolator) { this.interpolator = interpolator != null ? interpolator : new NoOpMessageInterpolator(); } }