Статьи

Разработка SOAP Web-сервиса с использованием Apache CXF

В последнем посте я рассмотрел шаги по разработке простого сервиса RESTFull с использованием apache CXF . В этой статье я расскажу о разработке веб-службы SOAP с использованием CXF. Прежде чем двигаться вперед, давайте разберемся с некоторыми концепциями / элементами, которые составляют веб-сервис SOAP.

SOAP или простой протокол доступа к объектам

SOAP — это протокол обмена сообщениями на основе XML по сети, использующий в качестве носителя протоколы приложений, такие как http, smtp и т. Д. Сообщение SOAP состоит из конверта SOAP. Конверт можно разбить на заголовок и тело. Заголовок содержит определения, связанные с контекстом, такие как безопасность, а тело содержит фактические данные приложения. Типичное сообщение SOAP выглядит так

01
02
03
04
05
06
07
08
09
10
<?xml version="1.0"?>
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope">
  <soap:Header>
  </soap:Header>
  <soap:Body>
    <m:GetStockPrice xmlns:m="http://www.example.org/stock">
      <m:StockName>IBM</m:StockName>
    </m:GetStockPrice>
  </soap:Body>
</soap:Envelope>

WSDL или язык описания веб-сервисов

WSDL — это стандартный язык XML, который используется для описания веб-службы. WSDL полностью описывает, какой публичный интерфейс предоставляет веб-служба, какой параметр он ожидает, структуру вывода, которую он возвращает, местоположение веб-службы. WSDL определяет веб-сервис как совокупность конечных точек связи, которые способны обмениваться сообщениями. Эти конечные точки связи называются портами. Порт состоит из двух частей.

  1. Содержит открытый интерфейс, предоставляемый веб-службой. Интерфейс содержит все методы, параметры, необходимые для их вызова, и структуру ответа, возвращаемую ими.
  2. Вторая часть связывает общедоступный интерфейс с сетевым протоколом, таким как http. Привязка содержит информацию, такую ​​как местоположение открытого интерфейса и формат сообщения для службы.

SOAP стили общения

Существует два типа стилей общения

  1. Документ
  2. RPC

Стиль связи, используемый веб-службой SOAP, определен в его WSDL.

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

В стиле RPC, как следует из названия, потребитель вызывает методы обслуживания, как если бы он вызывал локальный метод. Для этого сообщение RPC состоит из списка методов открытого интерфейса, которые может вызвать потребитель. Эти методы перечислены по именам как элементы xml. Параметры метода, необходимые для этого метода, образуют подэлементы элемента метода. Ответственность за маршалинг / демаршалинг лежит в основе веб-сервиса. Каркас содержит свои собственные библиотеки маршалинга / демаршалинга. Стиль RPC приводит к тесно связанному коду между кодом приложения и структурой веб-службы, поэтому нормой является создание служб стиля документа. Имея ключевые концепции, давайте рассмотрим пример написания мыльного веб-сервиса с использованием Apache CXF.

Получение исходного кода для этого урока

Я передал исходные файлы для этого урока в SVN.

Примечание. Оба проекта являются проектами ItelliJ maven, поэтому вы можете напрямую импортировать их в вашу IDE IntelliJ или вручную скопировать файлы в другие IDE.

Создайте скелетное приложение struts2, в котором будет содержаться ваш сервис.

Вы можете использовать любой MVC-фреймворк, но я предпочитаю struts2 по моим собственным причинам. Вы можете увидеть пример того, как создать пустое приложение struts2 в eclipse, используя maven здесь .

Добавить зависимости CXF

В вашем проекте POM добавьте следующие зависимости для загрузки CXF jars

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<properties>
       <cxf.version>2.5.0</cxf.version>
</properties>
<dependencies>
       <!-- apache cxf -->
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-rt-frontend-jaxws</artifactId>
            <version>${cxf.version}</version>
        </dependency>
 
        <dependency>
          <groupId>org.apache.cxf</groupId>
          <artifactId>cxf-rt-frontend-jaxrs</artifactId>
          <version>${cxf.version}</version>
       </dependency>
 
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-rt-transports-http</artifactId>
            <version>${cxf.version}</version>
        </dependency>
</dependencies>

Например, давайте создадим простой веб-сервис книжной полки. Для простоты предположим следующий вариант использования.

  1. Вставить книгу в книгу себя
  2. Получить книгу с книжной полки по названию.

Разработка сервиса

Это можно сделать двумя способами: сначала код и сначала контракт. Мы будем использовать код первый подход.

Создание интерфейса конечной точки службы (SEI)

Давайте создадим интерфейс SEI под названием BookShelfService

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
package com.aranin.weblog4j.services;
 
import com.aranin.weblog4j.vo.BookVO;
 
import javax.jws.WebMethod;
import javax.jws.WebService;
 
@WebService
public interface BookShelfService {
 
    @WebMethod
    public  String insertBook(BookVO bookVO);
    @WebMethod
    public  BookVO getBook(String title);
}

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

  • @WebService — это библиотека аннотаций JAXWS. Это превращает обычный POJO в веб-сервис. В нашем случае аннотация помещается прямо над определением интерфейса и уведомляет, что BookShelfService — это не обычный интерфейс, а интерфейс веб-службы или SEI. У этой аннотации есть и другие атрибуты, которые могут полностью определять веб-сервис, но мы не будем его сейчас использовать.
  • @WebMethod — эта аннотация является необязательной и в основном используется для предоставления атрибута имени публичному методу в wsdl.

Реализация сервиса.

Теперь у нас есть наш SEI, поэтому давайте реализуем методы интерфейса в нашем BookShelfServiceImpl

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
package com.aranin.weblog4j.services;
 
import com.aranin.weblog4j.hashdb.HashDB;
import com.aranin.weblog4j.vo.BookVO;
 
import javax.jws.WebService;
 
@WebService(endpointInterface = "com.aranin.weblog4j.services.BookShelfService",
        serviceName="bookShelfService")
public class BookShelfServiceImpl implements BookShelfService {
    public String insertBook(BookVO bookVO) {
        HashDB.insertBook(bookVO);
        return "Book with name : " + bookVO.getBookName() + " is now available on the shelf"//To change body of implemented methods use File | Settings | File Templates.
    }
 
    public BookVO getBook(String title) {
 
        return HashDB.getBook(title);  //To change body of implemented methods use File | Settings | File Templates.
    }
}

Этот класс является простым POJO, реализующим SEI. Единственная заметная вещь здесь — аннотация @WebService. Если вы внимательно посмотрите, мы предоставили полное имя класса SEI, который он реализует, и имя веб-службы.

Класс привязки данных (BookVO)

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package com.aranin.weblog4j.vo;
 
import javax.xml.bind.annotation.XmlRootElement;
import java.io.Serializable;
 
@XmlRootElement(name = "Book")
public class BookVO implements Serializable {
 
    private long bookId;
    private String bookName;
    private String author;
 
    public long getBookId() {
        return bookId;
    }
 
    public void setBookId(long bucketId) {
        this.bookId = bookId;
    }
 
    public String getBookName() {
        return bookName;
    }
 
    public void setBookName(String bookName) {
        this.bookName = bookName;
    }
 
    public String getAuthor() {
        return author;
    }
 
    public void setAuthor(String author) {
        this.author = author;
    }
}

Единственное, на что следует обратить внимание, это аннотация @XmlRootElement. Эта аннотация является частью библиотеки JAXB. CXF использует JAXB в качестве компонента привязки данных по умолчанию. Поскольку BookVO необходимо транспортировать как XML во время вызовов веб-службы, следовательно, он должен маршалировать / демаршалировать механизм JAXB в установке CXF. Используя аннотацию @XmlRootElement, мы помогаем JAXB отображать класс BookVO в xml с атрибутом name в качестве корневого элемента xml.

Spring Based Server Bean

Что делает CXF лучшим выбором в качестве среды веб-сервиса, так это то, что он публикует свои конечные точки службы через файл конфигурации на основе пружины. Давайте создадим файл конфигурации и зарегистрируем наш сервис в нем. Мы назовем файл как beans.xml и сохраним его в папке WEB-INF нашего приложения.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?xml version="1.0" encoding="UTF-8"?>
    xmlns:jaxws="http://cxf.apache.org/jaxws"
    xmlns:jaxrs="http://cxf.apache.org/jaxrs"
    xsi:schemaLocation="
 
 
 
 
 
 
 
<import resource="classpath:META-INF/cxf/cxf.xml" />
<import resource="classpath:META-INF/cxf/cxf-extension-soap.xml" />
<import resource="classpath:META-INF/cxf/cxf-servlet.xml" /> 
 
<jaxws:endpoint
       id="bookShelfService"
       implementor="com.aranin.weblog4j.services.BookShelfServiceImpl"
       address="/bookshelfservice" />
 
</beans>

Теперь для загрузки beans.xml мы просто добавляем следующее в web.xml

1
2
3
4
<context-param>
<param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/beans.xml,/WEB-INF/applicationContext.xml</param-value>
</context-param>

Наконец, нам нужно подключить Spring и CXF через web.xml.

01
02
03
04
05
06
07
08
09
10
11
12
13
<servlet>
       <servlet-name>CXFServlet</servlet-name>
       <display-name>CXF Servlet</display-name>
    <servlet-class>
        org.apache.cxf.transport.servlet.CXFServlet
    </servlet-class>
    <load-on-startup>2</load-on-startup>
</servlet>
 
<servlet-mapping>
    <servlet-name>CXFServlet</servlet-name>
    <url-pattern>/*</url-pattern>
</servlet-mapping>

Примечание: я не включил загрузку Spring ContextLoaderListner. Если вы создаете приложение struts2 через maven, используя начальный архетип struts2, то Spring загружается и регистрируется самим проектом maven.

Теперь ваш веб-сервис готов. Скомпилируйте и разверните приложение в любом контейнере сервлета. Если все хорошо, вы можете увидеть свой wsld в следующем месте: http: // localhost: 8080 / weblog4jdemo / bookshelfservice? Wsdl

Создайте своего клиента

Есть много инструментов, которые можно использовать для генерации клиентского кода с использованием wsdl. Чтобы избавить вас от дальнейших неприятностей, мы будем использовать собственный интерфейсный интерфейс CXF. Итак, давайте посмотрим на шаги.

  1. Создайте простой проект Maven, используя IDE по вашему выбору. Я использую IntelliJ в настоящее время, и это здорово. Допустим, название проекта DemoClient.
  2. Добавьте зависимости CXF, как показано в разделе «Создание каркасного приложения».
  3. Поскольку мы знаем, что такое SEI, публичные методы и объекты привязки. Мы создадим их на стороне клиента, чтобы избавить нас от неприятностей. Если таких классов много, мы можем использовать такие инструменты, как wsdl2java и т. Д., Чтобы сгенерировать наш код.
  4. Создайте заглушку SEI в точно такой же структуре пакета, что и родительский SEI.
  5. Создайте BookVO в той же структуре пакета, что и родительский BookVO.
  6. Вышеуказанные классы должны быть точно такими же, как вы создали в родительском приложении.
  7. Нам не нужно создавать реализацию SEI на стороне клиента.
  8. Теперь мы создадим клиент, используя JaxWsProxyFactoryBean. Этот класс является фабрикой, которая работает с прокси-серверами SEI для вызова методов веб-службы. Вот класс.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
package com.aranin.weblog4j.client;
 
import com.aranin.weblog4j.services.BookShelfService;
import com.aranin.weblog4j.vo.BookVO;
import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;
 
public class DemoClient {
    public static void main(String[] args){
        String serviceUrl = "http://localhost:8080/weblog4jdemo/bookshelfservice";
        JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
        factory.setServiceClass(BookShelfService.class);
        factory.setAddress(serviceUrl);
        BookShelfService bookService = (BookShelfService) factory.create();
 
        //insert book
        BookVO bookVO = new BookVO();
        bookVO.setAuthor("Issac Asimov");
        bookVO.setBookName("Foundation and Earth");
 
        String result = bookService.insertBook(bookVO);
 
        System.out.println("result : " + result);
 
        bookVO = new BookVO();
        bookVO.setAuthor("Issac Asimov");
        bookVO.setBookName("Foundation and Empire");
 
        result = bookService.insertBook(bookVO);
 
        System.out.println("result : " + result);
 
        bookVO = new BookVO();
        bookVO.setAuthor("Arthur C Clarke");
        bookVO.setBookName("Rama Revealed");
 
        result = bookService.insertBook(bookVO);
 
        System.out.println("result : " + result);
 
        //retrieve book
 
        bookVO = bookService.getBook("Foundation and Earth");
 
        System.out.println("book name : " + bookVO.getBookName());
        System.out.println("book author : " + bookVO.getAuthor());
 
    }
}

Вот вывод вышеуказанных звонков

1
2
3
4
5
6
7
8
INFO: Creating Service {http://services.weblog4j.aranin.com/}BookShelfServiceService from class com.aranin.weblog4j.services.BookShelfService
result : Book with name : Foundation and Earth is now available on the shelf
result : Book with name : Foundation and Empire is now available on the shelf
result : Book with name : Rama Revealed is now available on the shelf
book name : Foundation and Earth
book author : Issac Asimov
 
Process finished with exit code 0

В Apache CXF можно найти множество других вещей, таких как создание динамических клиентов, перехватчиков, использование другого транспортного протокола, веб-сервис через https и т. Д. Но я намерен написать этот пост как учебное пособие по началу работы.

Фу это снова длинный пост. Мне нужно улучшить свои навыки письма, чтобы сократить длину. Но все же я надеюсь, что вам понравилось, и вы нашли это полезным. Я намереваюсь написать о клиенте javascript для веб-сервисов в моем следующем посте. До тех пор, пока до свидания и счастливого кодирования.