Пользовательские сообщения об ошибках проверки с использованием Jaxb2marshaller и spring-ws

У меня есть работающий проект spring-ws, который может разбирать запросы с помощью Jax2b, но когда не удается разобрать целые/логические значения, я получаю сообщение об ошибке с небольшими подробностями и часто без имени недопустимого элемента. Например.:

org.springframework.oxm.UnmarshallingFailureException: JAXB unmarshalling exception; nested     exception is javax.xml.bind.UnmarshalException
 - with linked exception:
[org.xml.sax.SAXParseException; cvc-datatype-valid.1.2.1: '' is not a valid value for 'integer'.]

Это также становится содержимым ответа SOAPFault от моего веб-сервиса.

Я пытаюсь изменить сообщение, чтобы включить имя элемента. Я использую ValidationEventHandler для изменения сообщения, вызывая исключение RuntimeException из обработчика событий, но это работает только в некоторых случаях.

ValidationEventHandler:

@Component
public class ValidationEventHandlerImpl implements ValidationEventHandler {

    @Override
    public boolean handleEvent(ValidationEvent event) {

        String message = event.getMessage();
        String linkedMessage = "";
        if(event.getLinkedException() != null)
            linkedMessage = event.getLinkedException().toString();

        boolean ignoreValidationEvent = true;            
        if(message.contains("NumberFormatException") || 
                message.contains("is not a valid value") || 
                linkedMessage.contains("NumberFormatException") || 
                linkedMessage.contains("is not a valid value")){
            ignoreValidationEvent = false;
        }

        if(ignoreValidationEvent){
            return true;
        }else{
            String nodeName = "";
            if(event.getLocator() != null && event.getLocator().getNode() != null)
                nodeName = event.getLocator().getNode().getNodeName();

            //This is the important line
            throw new RuntimeException("Error parsing '" + nodeName + "': " + event.getMessage());

        }
    }

}

Он успешно меняет:

JAXB unmarshalling exception; nested exception is javax.xml.bind.UnmarshalException: Not a number: 32g321
 - with linked exception:
[java.lang.NumberFormatException: Not a number: 32g321]

to: Сообщение RuntimeException: "java.lang.RuntimeException: Ошибка анализа "MyNodeName": Не число: 32g321"

(Серьезность события: ОШИБКА)

Но это не работает, когда я хочу его изменить:

JAXB unmarshalling exception; nested exception is javax.xml.bind.UnmarshalException
 - with linked exception:
[org.xml.sax.SAXParseException; cvc-datatype-valid.1.2.1: '' is not a valid value for 'integer'.]

to: Сообщение RuntimeException: "java.lang.RuntimeException: Ошибка синтаксического анализа 'MyNodeName': '' не является допустимым значением для 'integer'".

Исключение RuntimeException игнорируется, а вместо него создается исключение SAXParseException, которое добавляется в ответ SOAPFault.

(Серьезность события: FATAL_ERROR)

Конфигурация Spring для Jaxb2Marshalling:

<bean id="jaxb2MarshallerContact" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
    <property name="classesToBeBound">
        <list>
            <value>com.model.oxm.ContactRequest</value>
            <value>com.model.oxm.ContactResponse</value>
        </list>
    </property>
    <property name="marshallerProperties">
        <map>
            <entry>
                <key>
                    <util:constant static-field="javax.xml.bind.Marshaller.JAXB_FORMATTED_OUTPUT" />
                </key>
                <value type="boolean">true</value>
            </entry>
            <entry>
                <key>
                    <util:constant static-field="javax.xml.bind.Marshaller.JAXB_FRAGMENT" />
                </key>
                <value type="boolean">true</value>
            </entry>
        </map>
    </property>
    <property name="schema" ref="ContactServiceSchema" />
    <property name="validationEventHandler" ref="validationEventHandlerImpl" />
</bean>

<bean id="ContactServiceSchema" class="org.springframework.core.io.ClassPathResource">
    <constructor-arg value="WEB-INF/schemas/ContactService.xsd" />
</bean> 

Конечная точка:

@Endpoint
public class ContactEndpoint {

    private Logger logger = Logger.getLogger(ContactEndpoint.class);

    @Autowired
    private ContactService contactService;

    private static final String NAMESPACE_URI = "http://mydomain/schemas";

    @PayloadRoot(namespace = NAMESPACE_URI, localPart = "ContactRequest")     
    @ResponsePayload
    public ContactResponse handleContactRequest(@RequestPayload ContactRequest contactRequest) throws Exception {
    ...
  • Как я могу вернуть собственное сообщение вместо сообщения SAXParseException?

  • Есть ли лучший способ реализовать это, например. используя ValidationErrorHandler?

Спасибо!


person molholm    schedule 06.08.2012    source источник
comment
Я не нашел хорошего решения этой проблемы. Поскольку это некритическая проблема в текущем приложении, и можно увидеть имя элемента, который не прошел проверку в журнале. Я решил не заниматься этим дальше... Предложения по-прежнему приветствуются!   -  person molholm    schedule 03.09.2012


Ответы (2)


Наконец-то я нашел способ обойти эту проблему. Вместо создания нового исключения RuntimeException из ValidationEventHandler я добавил его как подавленное исключение в исключении, связанном с событиями:

event.getLinkedException().addSuppressed(new RuntimeException(errorMessage));

и в конечной точке вместо этого я изменил RequestPayload на soapenvelope. marshallingService обертывает jax2bmarshaller:

@PayloadRoot(namespace = NAMESPACE_URI, localPart = "ContactRequest")     
@ResponsePayload
public ContactResponse handleContactRequest(@RequestPayload  SoapEnvelope soapEnvelope) throws Exception {
    ContactRequest contactRequest = marshallingService.unmarshalContact(soapEnvelope.getBody().getPayloadSource());

служба marshallingService перехватывает исключение, извлекает мое подавленное сообщение об исключении и выдает его вместо этого:

((UnmarshalException) xmlMappingException.getCause()).getLinkedException().getSuppressed()[0].getLocalizedMessage();

Это не элегантное решение, но конечная точка выдает гораздо более качественные сообщения об ошибках, чем раньше.

person molholm    schedule 14.03.2013

Вы пробовали использовать PayloadValidatingInterceptor? Насколько я знаю, это подтвердит сообщение перед демаршалингом. Возможно, это даст вам больше информации о том, что происходит не так.

Вы можете просто настроить его в конфигурации вашего приложения:

private ClassPathResource yourXsdFile = new ClassPathResource("xsd.xsd");

@Bean
public PayloadValidatingInterceptor validatingInterceptor() {
    PayloadValidatingInterceptor interceptor = new PayloadValidatingInterceptor();
    interceptor.setSchema(yourXsdFile);
    interceptor.setAddValidationErrorDetail(true);

    return interceptor;
}

@Bean
public PayloadRootAnnotationMethodEndpointMapping endpointMapping() {
    PayloadRootAnnotationMethodEndpointMapping mapping = new PayloadRootAnnotationMethodEndpointMapping();
    mapping.setInterceptors(new EndpointInterceptor[]{
        validatingInterceptor()
    });

    return mapping;
}

Для получения дополнительной информации посетите документация API.

person evandongen    schedule 04.02.2013
comment
Спасибо. Прошло некоторое время с тех пор, как я смотрел на это, но я попробую это как можно скорее. - person molholm; 05.02.2013
comment
У меня, вероятно, был результат поиска на моем экране, который не содержал недавних вопросов. Извините, что копаюсь в старом ;) - person evandongen; 05.02.2013
comment
Я просмотрел свои старые материалы и обнаружил, что уже пытался подключить пользовательский PayloadValidatingInterceptor для проверки входящих запросов. Не могу сказать, что это тупик, но мне с ним не очень повезло. - person molholm; 12.03.2013