Статьи

Wildfly, Apache CXF и @SchemaValidation

Последние несколько дней я работал над переносом приложения с JBoss 4 на Wildfly 8 . В приложении используются разные технологии, но мы сосредоточимся здесь на XML Web Services, JAX-WS . Да, я знаю, что они больше не модные, но они были разработаны давно и должны быть сохранены для проблем совместимости.

В любом случае, путь для миграции этих сервисов был не так прост. Я делюсь некоторыми проблемами и исправлениями с надеждой, что они могут помочь другим разработчикам, столкнувшимся с такими же проблемами.

Пример определения

Вот пример определения Web-сервиса в старой системе, JBoss 4:

1
2
3
4
5
6
@javax.jws.WebService(endpointInterface = "some.pack.age.WebService")
@javax.jws.soap.SOAPBinding(style = SOAPBinding.Style.DOCUMENT)
@org.jboss.ws.annotation.EndpointConfig(configName = "Standard WSSecurity Endpoint")
@javax.jws.HandlerChain(file = "handlers.xml")
@org.jboss.ws.annotation.SchemaValidation(enabled = true, errorHandler = CustomErrorHandler.class)
public class WebServiceImpl implements WebService {

К счастью, большая часть определения использует стандартные аннотации Java EE. Только @org.jboss.ws.annotation.EndpointConfig и @org.jboss.ws.annotation.SchemaValidation взяты из старых библиотек JBossWS.

Мы можем легко избавиться от @org.jboss.ws.annotation.EndpointConfig так как он нам не понадобится в новом приложении. Для справки: он используется для установки дополнительных данных конфигурации, которые должны быть предварительно определены с конечной точкой. Проверьте документацию Предопределенные конфигурации клиента и конечной точки .

Мы хотим сохранить @org.jboss.ws.annotation.SchemaValidation . Для справки, эта аннотация проверяет входящие и исходящие сообщения SOAP по соответствующей схеме в контракте wsdl конечной точки. Поскольку аннотации больше не существует в JBossWS, мы должны использовать Apache CXF , который является базовой реализацией для JAX-WS на Wildfly .

Проблемы

Вот несколько проблем, с которыми я столкнулся:

СхемаВалидация Аннотация

Аннотация @org.jboss.ws.annotation.SchemaValidation больше не существует. Вы должны использовать аннотацию org.apache.cxf.annotations.SchemaValidation из Apache CXF .

Добавьте следующую зависимость Maven для использования аннотации Apache CXF :

1
2
3
4
5
6
<dependency>
    <groupId>org.apache.cxf</groupId>
    <artifactId>cxf-api</artifactId>
    <version>2.7.11</version>
    <scope>provided</scope>
</dependency>

Также обратите внимание, что в исходной аннотации мы можем определить свойство errorHandler . Старое приложение использовало пользовательский обработчик ошибок для установки пользовательского сообщения об ошибках проверки схемы. В новой аннотации нет эквивалента, поэтому нам нужно сделать это по-другому. Для воспроизведения старого поведения я использовал Apache CXF Interceptors . Создайте класс-перехватчик и расширьте AbstractPhaseInterceptor . Вот образец:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
public class SchemaValidationErrorInterceptor
        extends AbstractPhaseInterceptor<Message> {
    public SchemaValidationErrorInterceptor() {
        super(Phase.MARSHAL);
    }
 
    @Override
    public void handleMessage(Message message) throws Fault {
        Fault fault = (Fault) message.getContent(Exception.class);
        Throwable cause = fault.getCause();
        while (cause != null) {
            if (cause instanceof SAXParseException) {
                fault.setMessage("Invalid XML: " + fault.getLocalizedMessage());
                break;
            }
 
            cause = cause.getCause();
        }
    }
}

И вы можете использовать это так:

1
2
3
@org.apache.cxf.interceptor.OutFaultInterceptors(
    classes = SchemaValidationErrorInterceptor.class
)

Перехватчики используются как клиентами CXF, так и серверами CXF . Существуют входящие и исходящие цепочки перехватчиков, выполняемые для регулярной обработки, а также при возникновении ошибки. В этом случае мы хотим переопределить сообщение проверки схемы, поэтому нам нужно связать наш перехватчик в цепочке исходящих перехватчиков с ошибкой. Вы можете использовать аннотацию @OutFaultInterceptors для этого поведения. Каждая цепочка разбита на фазы. Вы определяете этап, на котором вы хотите, чтобы перехватчик запускался, передавая Phase.MARSHAL в конструктор. Существуют и другие фазы, но поскольку мы хотим изменить сообщение об ошибке, мы делаем это на этапе MARSHAL .

Разные WSDL

В старых веб-службах файл WSDL создавался автоматически во время развертывания. К сожалению, в некоторых ситуациях WSDL, сгенерированный JBoss 4 и Wildfly 8 , отличается. Это может вызвать проблемы с вашими внешними абонентами. В этом случае основная проблема заключалась в проверке схемы. Запросы, которые были действительны в JBoss 4, больше не действовали, когда выполнялись в Wildfly 8 .

Причина такого поведения была в целевых пространствах имен. Если вы используете аннотации @XmlRootElement pojos в своих параметрах веб-службы без определения свойства namespace в аннотации, JBoss 4 WS сгенерировал целевой элемент WSDL с черным пространством имен. Apache CXF будет использовать пространство имен по умолчанию веб-службы для привязки элементов WSDL, если они пустые. Для справки это делается в коде CXF: org.apache.cxf.jaxws.support.JaxWsServiceConfiguration#getParameterName .

Это можно исправить, изменив код CXF , но мы решили поместить старый сгенерированный файл WSDL в перенесенные источники приложений и включить его в дистрибутив. Это больше не генерируется автоматически, это означает, что нам нужно вручную генерировать WSDL, если мы изменим API. Мы должны быть осторожны, чтобы убедиться, что мы ничего не нарушаем в WSDL. Этот подход казался лучше, чем поддержка собственной версии CXF . Возможно, мы могли бы также исправить это, но мы считаем, что поведение JBoss 4 не было предназначено.

Запустить CXF

Чтобы использовать конкретные API из CXF , недостаточно иметь зависимость проекта от него. Фактически, первые несколько раз, когда я пытался внести изменения, казалось, ничего не связанного с CXF . Это происходит потому, что Wildfly ищет только стандартные аннотации Java EE JAX-WS . Чтобы все поведение CXF работало, мы должны сообщить Wildfly, что наше приложение зависит от CXF , даже если библиотеки уже находятся на сервере. Да, это немного сбивает с толку.

Приложение развернуто в файле EAR. Поэтому вам нужно создать jboss-deployment-structure.xml и добавить следующее содержимое:

1
2
3
4
5
6
7
8
9
<jboss-deployment-structure>
 
    <sub-deployment name="application.war">
        <dependencies>
            <module name="org.apache.cxf"/>
        </dependencies>
    </sub-deployment>
 
</jboss-deployment-structure>

Использование файла MANIFEST.MF в файле WAR, очевидно, не работает, если он развернут в файле EAR. Для получения дополнительной информации, пожалуйста, проверьте Class Loading в WildFly .

Если вы хотите использовать другие функции CXF , особенно те, которые связаны с Spring, все может быть немного сложнее. Загляните в этот пост: разные факты о JBoss. Факт 6: JBoss и CXF: матч, заключенный на небесах .

Окончательное определение

Это должно быть нашим окончательным определением для нашего веб-сервиса:

1
2
3
4
5
6
7
8
9
@WebService(
        wsdlLocation = "WebService.wsdl",
        endpointInterface = "some.pack.age.WebService"
)
@SOAPBinding(style = SOAPBinding.Style.DOCUMENT)
@HandlerChain(file = "/handlers.xml")
@SchemaValidation(type = SchemaValidation.SchemaValidationType.IN)
@OutFaultInterceptors(classes = SchemaValidationErrorInterceptor.class)
public class WebServiceImpl implements BDNSWebService {

Как видите, необходимые изменения для переноса веб-службы с JBoss 4 на Wildfly — это всего лишь несколько. Тем не менее, есть несколько мелких деталей, которые могут блокировать вас на долгое время, если вы не знаете детали. Может быть, у вас другая настройка и проблемы, с которыми вы сталкиваетесь, другие. Это также может помочь, если вы просто пытаетесь настроить CXF с Wildfly. Надеюсь, этот пост будет вам полезен.

Ссылка: Wildfly, Apache CXF и @SchemaValidation от нашего партнера по JCG Роберто Кортеса в блоге