/** * Copyright (C) 2012-2013 Selventa, Inc. * * This file is part of the OpenBEL Framework. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * The OpenBEL Framework is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with the OpenBEL Framework. If not, see <http://www.gnu.org/licenses/>. * * Additional Terms under LGPL v3: * * This license does not authorize you and you are prohibited from using the * name, trademarks, service marks, logos or similar indicia of Selventa, Inc., * or, in the discretion of other licensors or authors of the program, the * name, trademarks, service marks, logos or similar indicia of such authors or * licensors, in any marketing or advertising materials relating to your * distribution of the program or any covered product. This restriction does * not waive or limit your obligation to keep intact all copyright notices set * forth in the program as delivered to you. * * If you distribute the program in whole or in part, or any modified version * of the program, and you assume contractual liability to the recipient with * respect to the program or modified version, then you will indemnify the * authors and licensors of the program for any liabilities that these * contractual assumptions directly impose on those licensors and authors. */ package org.openbel.framework.ws.core; import static java.lang.String.format; import static org.openbel.framework.common.BELUtilities.*; import java.lang.reflect.Method; import java.util.Locale; import org.springframework.util.Assert; import org.springframework.ws.context.MessageContext; import org.springframework.ws.server.EndpointExceptionResolver; import org.springframework.ws.server.endpoint.AbstractEndpointExceptionResolver; import org.springframework.ws.server.endpoint.MethodEndpoint; import org.springframework.ws.server.endpoint.annotation.PayloadRoot; import org.springframework.ws.soap.SoapBody; import org.springframework.ws.soap.SoapMessage; /** * {@link CustomizeSoapFaultExceptionResolver} is a custom implementation of an * {@link EndpointExceptionResolver endpoint exception resolver} that prepends * the webservice operation name to the endpoint's exception message. * * @author Anthony Bargnesi {@code <abargnesi@selventa.com>} */ public class CustomizeSoapFaultExceptionResolver extends AbstractEndpointExceptionResolver { /** * The suffix of the endpoint's request object - {@value} */ private static final String ENDPOINT_REQUEST_SUFFIX = "Request"; /** * The fault string format - {@value} */ private static final String FAULT_STRING_FORMAT = "%s: %s"; /** * {@inheritDoc} This implementation extracts the webservice operation name * from the {@link PayloadRoot payload root annotation} defines on the * {@link MethodEndpoint method endpoint}. The webservice operation name is * prepended to the soap fault's "faultstring". * <p> * If the webservice operation name cannot be determined the soap fault's * "faultstring" will just contain the {@link Exception exception}'s * message. This can occur when: * <ul> * <li>the endpoint, <tt>ep</tt>, is not an instance of * {@link MethodEndpoint method endpoint}</li> * <li>the {@link PayloadRoot} is <tt>null</tt></li> * <li>the "localPart" property of the {@link PayloadRoot payload root} does * not end with * {@link CustomizeSoapFaultExceptionResolver#ENDPOINT_REQUEST_SUFFIX}</li> * </p> */ @Override protected boolean resolveExceptionInternal( MessageContext messageContext, Object ep, Exception ex) { // fail when the response is not a SoapMessage, because we would not be // able to attach fault to a soap response Assert.isInstanceOf(SoapMessage.class, messageContext.getResponse(), "CustomizeSoapFaultExceptionResolver requires a SoapMessage"); String exMsg = ex.getMessage(); String firstMsg = getFirstMessage(ex); String exStacktrace = getFirstStacktrace(ex); if (hasLength(firstMsg)) { if (!exMsg.equals(firstMsg) && !exMsg.contains(firstMsg)) { exMsg = exMsg.concat("\ncaused by:\n\n" + firstMsg); } } if (hasLength(exStacktrace)) { exMsg = exMsg.concat(format("\n\nstacktrace:\n\n%s\n", exStacktrace)); } // if following conditions, soap fault with ex message if (!MethodEndpoint.class.isInstance(ep)) { return createFault(messageContext, null, exMsg); } // find payload root's localPart or return soap fault with ex message final Method emtd = ((MethodEndpoint) ep).getMethod(); String localPart = null; PayloadRoot proot = emtd.getAnnotation(PayloadRoot.class); if (proot != null) { localPart = proot.localPart(); } else { return createFault(messageContext, null, exMsg); } // return soap fault with ex message if localPart does not end with // 'Request' if (!localPart.endsWith(ENDPOINT_REQUEST_SUFFIX)) { return createFault(messageContext, null, exMsg); } // extract endpoint operation name localPart = localPart.substring(0, localPart.indexOf(ENDPOINT_REQUEST_SUFFIX)); return createFault(messageContext, localPart, exMsg); } /** * Create the soap fault and attach to the {@link SoapBody} of the * {@link SoapMessage soap response}. * * @param msgctx {@link MessageContext}, the message context for this * webservice message * @param wsopName {@link String}, the webservice operation name, which can * be <tt>null</tt> * @param exMsg {@link String}, the endpoint {@link Exception exception} * message * @return */ private boolean createFault(final MessageContext msgctx, final String wsopName, final String exMsg) { final String faultstring; if (wsopName == null) { logger.warn("Sending SOAP fault without endpoint identification for: " + msgctx.getRequest()); faultstring = exMsg; } else { faultstring = format(FAULT_STRING_FORMAT, wsopName, exMsg); } logError(wsopName, exMsg); final SoapMessage response = (SoapMessage) msgctx.getResponse(); final SoapBody body = response.getSoapBody(); body.addServerOrReceiverFault(faultstring, Locale.ENGLISH); return true; } private void logError(String leader, final String msg) { if (leader == null) leader = "request"; logger.error(format("SOAP error encountered with %s: %s", leader, msg)); } }