JAVA / [Из песочницы] SOAP-сервер на Java при участии Apache CXF и Spring

в 9:13, , рубрики: ant, cxf, idea, java, maven, soap, soapui, spring framework, tomcat, wsdl, метки: , , , , , , , , ,

JAVA / [Из песочницы] SOAP-сервер на Java при участии Apache CXF и Spring
За последнее время появилось несколько статей, рассказывающих о протоколе SOAP, а также описывающих процесс создания сервера на различных языках и платформах. Продолжим тему. В этой статье будет описываться создание сервера на языке Java с использование Apache CXF и Spring Framework. Предполагается, что читатель уже имеет общее представление об упомянутом протоколе, а также о работе с ant и maven. Для того, чтобы сделать задачу немного интереснее, добавим начальное условие: дана WSDL-схема, описывающая веб-сервис. Итак…
(Картинка из статьи на Wikipedia.)
0. Начальные условия

Задача, которую перед нами поставили: необходимо реализовать веб-сервис, соответствующий спецификации SOAP 1.1, основываясь на готовой WSDL-схеме. В качестве протокола для передачи «конвертов» будем использовать HTTP. Таким образом нашу задачу можно разбить на две составляющие:создание java-интерфейса на основе WSDL-схемы;

реализация веб-сервиса на основе полученного интерфейса.

По первому пункту стоит сделать следующую ремарку. В частном случае мы могли бы вручную описать необходимые нам операции, однако в общем случае удобнее воспользоваться инструментом для автоматической генерации интерфейса.
Вот так выглядит исходная WSDL-схема:

Сервис имеет весьма простую функциональность: возвращает текущее время в указанном часовом поясе. В случае, когда пояс не указан, сервис возвращает текущее время сервера.
В процессе работы мы будем использовать следующие инструменты:
Apache CXF;

Apache Maven;

Apache Ant;

Apache Tomcat;

soapUI.

Все их вместе очень удобно использовать с помощью IntelliJ IDEA, но этот инструмент не является обязательным.
Структура проекта

Определим структуру проекта. В папке build будем хранить скрипты для ant. В модуле ObjectLibrary будем держать исходную схему, а также сгенерированные по ней классы. Модуль CurrentTimeService будет основным, в нем будем держать реализацию сервиса, также по нему будем собирать war-файл.
В pom-файлах укажем необходимые модулям зависимости. Внешних зависимостей у нас будет не много: spring-web, log4j, cxf-rt-frontend-jaxws и cxf-rt-transports-http — все они доступны в http://repo1.maven.org/maven2.
1. Создание java-интерфейса на основе WSDL-схемы

Для автоматической генерации java-интерфейса на основе WSDL-схемы воспользуемся утилитой wsdl2java из пакета Apache CXF. Немного модифицируем исходный ant-скрипт для того, чтобы он предварительно очищал директорию, в которую будут складываться java-классы. Это может быть полезно в том случае, если в будущем исходная схема будет меняться. По-умолчанию директория не очищается, что может приводить к появлению «мусора» из классов, которые больше не используются. Такая проблема характерна для схем, имеющих в своем составе сложные составные типы.

Разместим этот скрипт в файле build/build.xml. Запустим таску regenerate.object.library, в результате работы которой в папке ObjectLibrary получим необходимый нам интерфейс.
package me.artspb.cts;

import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
import javax.xml.bind.annotation.XmlSeeAlso;

/**
* This class was generated by Apache CXF 2.5.2
* 2012-02-02T17:39:35.466+04:00
* Generated source version: 2.5.2
*
*/
@WebService(targetNamespace = "http://artspb.me/cts", name = "CurrentTimeService")
@XmlSeeAlso({ObjectFactory.class})
@SOAPBinding(parameterStyle = SOAPBinding.ParameterStyle.BARE)
public interface CurrentTimeService {

@WebResult(name = "currentTime", targetNamespace = "http://artspb.me/cts", partName = "currentTime")
@WebMethod(action = "http://artspb.me/cts")
public javax.xml.datatype.XMLGregorianCalendar getCurrentTime(
@WebParam(partName = "timeZoneId", name = "timeZoneId", targetNamespace = "http://artspb.me/cts")
java.lang.String timeZoneId
);
}

Стоит иметь ввиду, что с помощью утилиты wsdl2java мы сразу могли бы получить готовый сервер (ключ -server) и даже реализацию для него (ключ -impl). Однако в рамках данной статьи мы будем использовать ее только для получения необходимого нам интерфейса, чтобы избежать жесткой привязки к сгенерированному коду.
2. Реализация веб-сервиса на основе полученного интерфейса

Реализацию интерфейса CurrentTimeService разместим в одноименном модуле. Код незамысловатый и в дополнительных комментариях не нуждается.
package me.artspb.cts;

import org.apache.log4j.Logger;

import javax.jws.WebParam;
import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.XMLGregorianCalendar;
import java.util.GregorianCalendar;
import java.util.TimeZone;

public class CurrentTimeServiceImpl implements CurrentTimeService {
private Logger logger = Logger.getLogger(CurrentTimeServiceImpl.class);

public XMLGregorianCalendar getCurrentTime(
@WebParam(partName = "timeZoneId", name = "timeZoneId", targetNamespace = "http://artspb.me/cts")
String timeZoneId) {

logger.debug("Operation getCurrentTime was requested.");

XMLGregorianCalendar gregorianCalendar;

try {
gregorianCalendar = getXmlGregorianCalendar(timeZoneId);
} catch (DatatypeConfigurationException e) {
throw new RuntimeException(e);
}

logger.debug("Successful.");

return gregorianCalendar;
}

private XMLGregorianCalendar getXmlGregorianCalendar(String id) throws DatatypeConfigurationException {
TimeZone timeZone;

if (!"".equals(id)) {
logger.debug("TimeZoneId isn't null: " + id);

timeZone = TimeZone.getTimeZone(id);
} else {
logger.debug("TimeZoneId is null. Will use default value.");

timeZone = TimeZone.getDefault();
}

GregorianCalendar gregorianCalendar = new GregorianCalendar(timeZone);

return DatatypeFactory.newInstance().newXMLGregorianCalendar(gregorianCalendar);
}
}

Теперь, в соответствии с примером, добавим в ресурсы файлы serviceContext.xml и web.xml. Также не забудем про log4j.xml. Найти эти файлы можно в архиве с исходными кодами, ссылка на него приведена в конце статьи. После этого реализацию сервиса можно считать законченной, осталось собрать приложение и проверить его работоспособносcть.
3. Сборка и проверка

Для сборки проекта выполним задачу maven «package». Полученный war-файл развернем на сервере Tomcat. Будем считать, что он доступен на порту 8080.
Для проверки воспользуемся программой soapUI. WSDL-схема сервиса доступна по ссылке http://localhost:8080/CurrentTimeService/service/currentTimeService?wsdl, с ее помощью мы можем создать новый проект soapUI.
Отправляем запрос:

PST

Получаем ответ:

2012-02-02T08:48:24.402-08:00

Вместо заключения

Поставленная задача достигнута: веб-сервис корректно обрабатывает запросы. Также нам удалось избежать «жесткой» привязки к генерируемой реализации, что делает код более гибким и позволяет прикладывать меньше усилий для внесения изменений в дальнейшем.
Успехов!
Основные ссылки вместе:
исходный код на Dropbox;

SOAP на Wikipedia;

WSDL на Wikipedia;

WSDL to Java;

Writing a service with Spring.


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js