Web-сервисы Java: JAXB и JAX-WS в Axis2

Создание Web-сервисов на основе Axis2 с использованием стандартных технологий JAXB2.x и JAX-WS 2.x

Apache Axis2 поддерживает целый ряд технологий для связывания с данными, в том числе JAXB 2.x, являющуюся стандартом в Java™. Кроме того, Axis2 предоставляет возможности для использования JAX-WS 2.x, стандартной Java-технологии для конфигурирования Web-сервисов, в качестве альтернативы собственному решению. В этой статье, являющейся продолжением серии "Web-сервисы Java", Деннис Сосноски демонстрирует примеры использования данных Java-стандартов вместе с Axis2, уделяя внимание существующим на данных момент ограничениям.

Денис Сосноски, консультант, Sosnoski Software Solutions, Inc.

Денис Сосноски (Dennis Sosnoski) - основатель и ведущий специалист консалтинговой компании по технологиям Java - Sosnoski Software Solutions, Inc., специализирующейся в обучении и консультировании по проблемам XML и Web-сервисов. Он имеет более чем 30-летний опыт работы в профессиональном проектировании ПО, специализируясь на серверных XML и Java-технологиях. Денис является основным разработчиком интегрированной системы с открытым программным кодом JiBX XML Data Binding, построенной на базе технологии классов Java и связанной системы Web-сервисов JibxSoap, также как и системы Web-сервисов Apache Axis2. Он также был одним их экспертов при разработке спецификаций JAX-WS 2.0.



16.02.2010

Об этой серии

Web-сервисы занимают одно из центральных мест в современных корпоративных Java-приложениях. В серии "Web-сервисы Java" консультант по этим технологиям Деннис Сосноски рассказывает об основных системах и решениях, представляющих интерес для Java-разработчиков, использующих Web-сервисы. Читая статьи этой серии, вы будете получать самую свежую информацию о последних разработках в данной области и о том, как их можно применить в ваших проектах.

Изначально Apache Axis базировался на первой стандартной Java-технологии для создания Web-сервисов — JAX-RPC. Однако, это оказалось не самым лучшим решением, поскольку JAX-RPC накладывал ограничения на внутреннюю архитектуру Axis, являясь причиной как недостаточной гибкости, так и недостаточной производительности. Кроме того, предположения на тему направлений развития Web-сервисов, сделанные в JAX-RPC, на практике оказались неверными.

К моменту начала создания Axis2 уже шла работа по замене JAX-RPC, вследствие чего в архитектуру Axis2 изначально была заложена возможность поддержки различных стандартных технологий для реализации Web-сервисов. В последних версиях Axis2 поддерживаются как JAXB 2.x (стандарт Java для связывания с данными), так и JAX-WS 2.x (стандарт Java, который пришел на смену JAX-RPC). Из этой статьи вы узнаете о применении JAXB 2.x и JAX-WS 2.x совместно с Axis2, а также об ограничениях, накладываемых текущей реализацией Axis2 на использование данных технологий.

JAXB в Axis2

JAXB 2.x является одной из нескольких технологий для связывания с данными, которую можно использовать в Axis2 при генерации исходного кода на Java по описанию сервиса на языке WSDL (Web Services Description Language). Другие подобные технологии рассматриваются в статье "Web-сервисы Java: связывание с данными в Axis2". При использовании большинства из них, генерация кода на Java на основе WSDL выполняется утилитой WSDL2Java, которая создает набор связующих классов и классов модели данных. Связующие классы, к которым относятся клиентские заглушки и классы для приема сообщений на стороне сервера, играют роль интерфейса между Axis2 и вашим приложением, в то время как классы модели служат для представления непосредственно сообщений.

В JAXB 2.x используются аннотации классов модели для управления преобразованием данных в XML и обратно. Такой подход позволяет использовать различные реализации JAXB без необходимости внесения изменения в исходный код и перекомпиляции приложения. При этом доступ к информации, представленной в аннотациях, и ее использование при конвертации данных является прерогативой используемой реализации JAXB.

В разделе Загрузка содержится ссылка на архив с исходным кодом демонстрационного приложения, использующего JAXB в Axis2 (см. каталог jaxb). Оно представляет собой очередной вариант простого библиотечного сервиса, который встречался в предыдущих статьях серии, в том числе, в статье "Связывание с данными в Axis2". Описание сервиса в WSDL определяет следующие четыре операции:

  • getBook - операция, возвращающая подробную информацию о книге по ее коду ISBN;
  • getBooksByType - операция, возвращающая информацию по всем книгам указанного типа;
  • getTypes - операция, возвращающая все типы имеющихся в наличии книг;
  • addBook - операция, служащая для добавления книги в библиотеку.

В листинге 1 показан модифицированный фрагмент WSDL, содержащий только информацию, имеющую отношение к операции getBook.

Листинг 1. Фрагмент описания библиотечного сервиса на языке WSDL
<wsdl:definitions targetNamespace="http://ws.sosnoski.com/library/wsdl"
    xmlns:wns="http://ws.sosnoski.com/library/wsdl"
    xmlns:tns="http://ws.sosnoski.com/library/types"
    xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
    xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/">
  <wsdl:types>
  
    <schema elementFormDefault="qualified"
        targetNamespace="http://ws.sosnoski.com/library/wsdl"
        xmlns="http://www.w3.org/2001/XMLSchema">
      
      <import namespace="http://ws.sosnoski.com/library/types"
          schemaLocation="types.xsd"/>
        
      <element name="getBook">
        <complexType>
          <sequence>
            <element name="isbn" type="string"/>
          </sequence>
        </complexType>
      </element>
      
      <element name="getBookResponse">
        <complexType>
          <sequence>
            <element name="getBookReturn" minOccurs="0" type="tns:BookInformation"/>
          </sequence>
        </complexType>
      </element>
      ...
    
    </schema>

  </wsdl:types>

  <wsdl:message name="getBookRequest">
    <wsdl:part element="wns:getBook" name="parameters"/>
  </wsdl:message>

  <wsdl:message name="getBookResponse">
    <wsdl:part element="wns:getBookResponse" name="parameters"/>
  </wsdl:message>
  ...

  <wsdl:portType name="Library">

    <wsdl:operation name="getBook">
      <wsdl:input message="wns:getBookRequest" name="getBookRequest"/>
      <wsdl:output message="wns:getBookResponse" name="getBookResponse"/>
    </wsdl:operation>
    ...

  </wsdl:portType>

  <wsdl:binding name="LibrarySoapBinding" type="wns:Library">

    <wsdlsoap:binding style="document"
      transport="http://schemas.xmlsoap.org/soap/http"/>

    <wsdl:operation name="getBook">
    
      <wsdlsoap:operation soapAction="urn:getBook"/>
      
      <wsdl:input name="getBookRequest">
        <wsdlsoap:body use="literal"/>
      </wsdl:input>
      
      <wsdl:output name="getBookResponse">
        <wsdlsoap:body use="literal"/>
      </wsdl:output>
      
    </wsdl:operation>
    ...

  </wsdl:binding>

  <wsdl:service name="jaxb-library">

    <wsdl:port binding="wns:LibrarySoapBinding" name="library">
      <wsdlsoap:address location="http://localhost:8080/axis2/services/jaxb-library"/>
    </wsdl:port>

  </wsdl:service>

</wsdl:definitions>

Поддержку JAXB в Axis2 планируется расширить, добавив возможность генерации неупакованных (unwrapped) методов (название обусловлено тем, что данные, упакованные внутри сообщения, передаются методам в виде обычных параметров; более детальное описание различий между упакованными и неупакованными интерфейсами приведено в статье "Связывание с данными в Axis2"). Однако, классы с неупакованными методами, сгенерированные при помощи WSDL2Java, нельзя использовать ни в текущем релизе, ни в нескольких предыдущих релизах Axis2, поэтому единственный вариант применения JAXB заключается в генерации упакованных интерфейсов (принципиально отличающийся подход описывается ниже, в разделе "Использование JAX-WS в Axis2"). Сервисные методы упакованных интерфейсов принимают единственный параметр типа Object, через который передается сообщение от клиента, и возвращают результат также типа Object, содержащий ответ сервиса.

Демонстрационное приложение содержит реализацию сервиса и тестового клиента, готовых к использованию классов, сгенерированных утилитой WSDL2Java. Как и в предыдущих статьях серии, архив включает файлы build.properties и build.xml, предназначенных для сборки приложения при помощи Apache Ant (они находятся в каталоге jaxb). Вначале вам следует отредактировать файл build.properties, указав в нем правильный путь к каталогу Axis2, а также при необходимости изменить другие настройки с учетом вашей системы. После этого вам останется только перейти в каталог jaxb и выполнить команду ant из командной строки. Это приведет к запуску WSDL2Java, компиляции всего исходного кода, в том числе сгенерированного, а также к созданию файла AAR для развертывания на сервере. В качестве проверки вы можете провести развертывание файла AAR на локальном сервере Axis2, а затем выполнить команду ant run.

Использование JAXB в клиентском приложении

Клиентское приложение создает объект-заглушку, играющую роль сервиса, с учетом параметром командной строки, а затем выполняет следующие пять вызовов методов сервиса:

  1. получение информации о книге;
  2. получение всех типов книг, находящихся в библиотеке;
  3. добавление книги в библиотеку (эта операция завершится неудачно в случае, если добавляемая книга уже находится в библиотеке, что может происходить если запускать клиентское приложение несколько раз без перезапуска сервера);
  4. добавление книги с тем же кодом ISBN, в случае если предыдущая операция завершилась успешно (данный вызов должен всегда приводить к ошибке);
  5. получение информации обо всех книгах заданного типа.

Исходный код тестового клиентского приложения полностью приведен в листинге 2. Как видите, используемый интерфейс сервиса является упакованным, поскольку данные в каждый метод передаются в виде единственного объекта-сообщения. Например, метод getTypes принимает на вход объект типа GetTypes (несмотря на то, что данная операция не требует входных параметров) и возвращает результат типа GetTypesResponse.

Листинг 2. Пример клиентского кода с использованием JAXB
public class WebServiceClient
{
    public static void main(String[] args) throws Exception {
        
        // проверка наличия параметров командной строки
        if (args.length < 3) {
            System.out.println("Usage:\n  java " +
                "com.sosnoski.ws.library.jaxb.WebServiceClient host port path");
            System.exit(1);
        }
        
        // создание клиентской заглушки
        String target = "http://" + args[0] + ":" + args[1] + args[2];
        System.out.println("Connecting to " + target);
        JaxbLibraryStub stub = new JaxbLibraryStub(target);
        
        // получение информации о книге
        String isbn = "0061020052";
        GetBook gb = new GetBook();
        gb.setIsbn(isbn);
        GetBookResponse gbr = stub.getBook(gb);
        BookInformation book = gbr.getGetBookReturn();
        if (book == null) {
            System.out.println("No book found with ISBN '" + isbn + '\'');
        } else {
            System.out.println("Retrieved '" + book.getTitle() + '\'');
        }
        
        // получение списка типов книг
        GetTypesResponse gtr = stub.getTypes(new GetTypes());
        List<TypeInformation> types = gtr.getGetTypesReturn();
        System.out.println("Retrieved " + types.size() + " types:");
        for (int i = 0; i < types.size(); i++) {
            TypeInformation type = types.get(i);
            System.out.println(" '" + type.getName() + "' with " +
                type.getCount() + " books");
        }
        
        // добавление книги
        String title = "The Dragon Never Sleeps";
        isbn = "0445203498";
        try {
            AddBook ab = new AddBook();
            ab.setType("scifi");
            ab.setIsbn(isbn);
            ab.getAuthor().add("Cook, Glen");
            ab.setTitle(title);
            stub.addBook(ab);
            System.out.println("Added '" + title + '\'');
            title = "This Should Not Work";
            ab.setTitle(title);
            stub.addBook(ab);
            System.out.println("Added duplicate book - should not happen!");
        } catch (AddDuplicateFault e) {
            System.out.println("Failed adding '" + title +
                "' with ISBN '" + isbn + "' - matches existing title '" +
                e.getFaultMessage().getBook().getTitle() + '\'');
        }
        
        // получение списка всех книг заданного типа
        GetBooksByType gbbt = new GetBooksByType();
        gbbt.setType("scifi");
        GetBooksByTypeResponse gbbtr = stub.getBooksByType(gbbt);
        List<BookInformation> books = gbbtr.getGetBooksByTypeReturn();
        System.out.println("Retrieved " + books.size() + " books of type 'scifi':");
        for (int i = 0; i < books.size(); i++) {
            System.out.println(" '" + books.get(i).getTitle() + '\'');
        }
    }
}

Если сравнить листинг 2 с примерами клиентского кода в статье "Web-сервисы Java: связывание с данными в Axis2", то легко заметить его сходство с упакованными интерфейсами JiBX и ADB (Axis Data Binding), причем основное различие заключается в том, что классы-обертки в JAXB используют типизированные списки Java 5 вместо массивов (подобная возможность также поддерживается в JiBX, но не в ADB).

Использование JAXB на стороне сервера

Серверная часть библиотечного сервиса состоит из двух классов, первый из которых реализует необходимые операции, а второй служит для адаптации интерфейса в соответствии с требованиями Axis2. Реализация сервиса практически не зависит от используемой технологии связывания с данными за исключением незначительных вариаций в сгенерированных классах модели данных. Класс, выполняющий функции интерфейса сервиса, представляет больший интерес (листинг 3). Как и клиентское приложение, он должен распаковывать получаемые сообщения и упаковывать отправляемые клиентам результаты. 

Листинг 3. Серверный код, использующий JAXB
public class JaxbLibraryImpl extends JaxbLibrarySkeleton
{
    private final BookServer m_server;
    
    public JaxbLibraryImpl() {
        m_server = new BookServer();
    }
    
    public AddBookResponse addBook(AddBook req) throws AddDuplicateFault {
        BookInformation prior = m_server.getBook(req.getIsbn());
        if (prior == null) {
            BookInformation book = new BookInformation();
            book.getAuthor().addAll(req.getAuthor());
            book.setIsbn(req.getIsbn());
            book.setTitle(req.getTitle());
            book.setType(req.getType());
            AddBookResponse rsp = new AddBookResponse();
            rsp.setAddBookReturn(m_server.addBook(book));
            return rsp;
        } else {
            AddDuplicateFault e =
                new AddDuplicateFault("Book already present with matching ISBN");
            AddDuplicate ad = new AddDuplicate();
            ad.setBook(prior);
            e.setFaultMessage(ad);
            throw e;
        }
    }

    public GetBookResponse getBook(GetBook req) {
        BookInformation book = m_server.getBook(req.getIsbn());
        GetBookResponse rsp = new GetBookResponse();
        rsp.setGetBookReturn(book);
        return rsp;
    }

    public GetBooksByTypeResponse getBooksByType(GetBooksByType req) {
        GetBooksByTypeResponse rsp = new GetBooksByTypeResponse();
        rsp.getGetBooksByTypeReturn().addAll(m_server.getBooksByType(req.getType()));
        return rsp;
    }

    public GetTypesResponse getTypes(GetTypes req) {
        GetTypesResponse rsp = new GetTypesResponse();
        rsp.getGetTypesReturn().addAll(m_server.getTypes());
        return rsp;
    }
}

В статье "Java Web Services: Axis2 Data Binding" не демонстрировались примеры интерфейсного кода сервиса для различных технологий связывания с данными. Однако если сравнить класс в листинге 3 с интерфейсными классами в архиве с исходным кодом к той статье, то вы увидите, что он очень напоминает классы, использующиеся при работе с упакованными интерфейсами ADB и JiBX. Как и ранее, разница сводится к использованию типизированных списков вместо массивов. 

Классы модели данных

В листинге 4 показаны некоторые их JAXB-классов модели данных, генерируемых утилитой WSDL2Java (большинство автоматически сгенерированных комментариев было удалено, за исключением нескольких, которые были оставлены в качестве иллюстрации). Между клиентскими и серверными классами модели нет различий, хотя они генерируются отдельно друг от друга при сборке приложения. Показанные в данном примере классы используются на стороне клиента при вызове сервисного метода getBook в листинге 2, а также на стороне сервера в листинге 3. Все классы и практически все атрибуты содержат аннотации (выделены жирным шрифтом), в которых указывается конфигурационная информация, используемая JAXB для преобразования объектов в XML и обратно.

Листинг 4. Классы модели данных JAXB
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
    "isbn"
})
@XmlRootElement(name = "getBook")
public class GetBook {

    @XmlElement(required = true)
    protected String isbn;

    /**
     * Возвращает значение свойства ISBN
     * 
     * @return
     *     possible object is
     *     {@link String }
     *     
     */
    public String getIsbn() {
        return isbn;
    }

    /**
     * Устанавливает значение свойства ISBN
     * 
     * @param value
     *     allowed object is
     *     {@link String }
     *     
     */
    public void setIsbn(String value) {
        this.isbn = value;
    }
}

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "BookInformation", propOrder = {
    "author",
    "title"
})
public class BookInformation {

    protected List<String> author;
    @XmlElement(required = true)
    protected String title;
    @XmlAttribute(required = true)
    protected String type;
    @XmlAttribute(required = true)
    protected String isbn;

    public List<String> getAuthor() {
        if (author == null) {
            author = new ArrayList<String>();
        }
        return this.author;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String value) {
        this.title = value;
    }

    public String getType() {
        return type;
    }

    public void setType(String value) {
        this.type = value;
    }
    
    public String getIsbn() {
        return isbn;
    }

    public void setIsbn(String value) {
        this.isbn = value;
    }
}

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
    "getBookReturn"
})
@XmlRootElement(name = "getBookResponse")
public class GetBookResponse {

    protected BookInformation getBookReturn;

    public BookInformation getGetBookReturn() {
        return getBookReturn;
    }

    public void setGetBookReturn(BookInformation value) {
        this.getBookReturn = value;
    }
}

Аннотация @XmlAccessorType, применимая к классам и пакетам, описывает способ обращения к атрибутам классов. При этом поддерживаются следующие варианты: прямой доступ к переменным-членам класса, доступ через get-/set-методы свойств, доступ только к открытым членам и свойствам, а также доступ только к атрибутам, отмеченным собственными аннотациями. При помощи аннотации @XmlType, применимой к классам и перечислениям, указывается соответствующий им тип элементов схемы XML и порядок полей в его представлении. Кроме того, при необходимости можно указать JAXB, что экземпляры Java-класса следует создавать с использованием класса-фабрики. Аннотация @XmlRootElement  также применяется к классам и перечислениям, которые должны быть связаны с корневым элементом в XML, указывая имя и пространство имен последнего. Наконец, аннотации @XmlElement и @XmlAttribute  применяются к полям и методам свойств классов JavaBean, задавая имена XML-элементов, атрибутов и другие параметры.

Классы всех аннотаций, используемых в JAXB (в том числе и множество тех, которые не вошли в пример выше), находятся в пакете javax.xml.bind.annotation. JAXB позволяет генерировать как код классов на основе схемы XML, так и XML на основе существующего кода. Некоторые аннотации и их возможности, в частности, указание методов сериализации и десериализации, а также создание объектов при помощи фабрик, могут использоваться только во втором случае.

Проблемы при использовании JAXB в Axis2

В основном процесс генерации классов модели данных является независимым от Axis2, поскольку WSDL2Java использует компилятор связывания XJC, являющийся стандартным компонентом JAXB. Таким образом, запустив XJC напрямую для схем, используемых Web-сервисом, вы получите те же самые классы модели. К сожалению, взаимодействие между WSDL2Java и XJC не всегда бывает идеальным, что приводит к определенным проблемам.

Одна из таких проблем связана со структурой схем в документах WSDL. Например, библиотечный сервис изначально использовал единый документ WSDL, состоявший из двух схем, одна из которых описывала элементы сообщений WSDL, а другая — структуру данных приложения (информацию о книгах и их типах). При этом схема элементов сообщений импортировала прикладную схему стандартными средствами WSDL, а именно, через ссылку на ее пространство имен. Подобные составные документы WSDL, содержащие вложенные схемы, не представляют проблем для WSDL2Java при использовании ADB или JiBX, однако в случае JAXB генерируется исключение на этапе обработки схем. Эту проблему можно решить, выделив схему данных приложения в отдельный файл и указав его имя в инструкции импорта.

Другая проблема заключается в том, что хотя XJC предоставляет возможности для настройки процесса генерации кода, в частности, управление генерацией классов, соответствующих указанным компонентам схемы, WSDL2Java не позволяет передавать эти параметры в XJC, в результате чего всегда используется конфигурация по умолчанию. Таким образом, если вам необходимо управлять процессом генерации кода, то следует запускать XJC отдельно от WSDL2Java. К сожалению, на текущий момент нельзя использовать отдельно созданную модель данных JAXB в процессе генерации кода при помощи WSDL2Java. Если это все же необходимо, то, вероятно, лучше всего будет сгенерировать модель данных WSDL2Java, а затем заменить классы модели и вручную произвести все необходимые корректировки. Альтернативный подход заключается в использовании технологии JAX-WS, которая описывается в следующем разделе. При этом необходимость в WSDL2Java не возникает вообще, однако это преимущество достигается ценой наложения других серьезных ограничений.


Использование JAX-WS в Axis2

В отличие от JAXB, который можно рассматривать как очередной вариант связывания с данными в Axis2, JAX-WS представляет собой принципиально другой подход к описанию Web-сервисов. Эта технология способна полностью заменить стандартные средства клиентской и серверной конфигурации в Axis2. При использовании JAX-WS генерация кода Java на основе документов WSDL осуществляется при помощи утилиты WsImport (она входит в состав реализации JAX-WS, которую необходимо загрузить отдельно от Axis2, см. Ресурсы), а не WSDL2Java. Более того, даже механизм развертывания приложений JAX-WS отличается от стандартного подхода, на основе файлов AAR, который обычно используется в Axis2.

В архиве с примерами к статье содержится вторая версия библиотечного приложения, призванная продемонстрировать использование JAX-WS в Axis2. Его код находится в каталоге jaxws и включает собственные файлы WSDL, а также build.properties и and build.xml. Документ WSDL практически эквивалентно тому, который применялся вместе с JAXB (см. листинг 1), за тем исключением, что он содержит вложенную схему данных приложения, что было невозможно при использовании WSDL2Java для генерации модели данных JAXB.

Запустив утилиту WsImport для генерации кода на основе WSDL, вы получите ту же модель данных JAXB и классы-обертки, что и ранее, при помощи WSDL2Java. Различия заключаются в коде связывания, который в случае JAX-WS состоит из сгенерированного интерфейса сервиса и клиентского класса-конструктора сервиса. Интерфейсный класс предоставляет методы, соответствующие операциям, описанным в WSDL (его отформатированный код приведен в листинге 5; все комментарии были удалены за исключением одного, оставленного в качестве иллюстрации). Этот класс используется как на клиентской, так и на серверной стороне. Он отмечен множеством аннотаций, которые содержат всю информацию, необходимую JAX-WS для связывания интерфейса с сервисом, в частности, для сопоставления методов класса и операций сервиса.

Листинг 5. Сгенерированный интерфейс сервиса JAX-WS
@WebService(name = "Library", targetNamespace = "http://ws.sosnoski.com/library/wsdl")
@XmlSeeAlso({
    ObjectFactory.class
})
public interface Library
{
    /**
     * 
     * @param isbn
     * @return
     *     returns com.sosnoski.ws.library.jaxws.BookInformation
     */
    @WebMethod(action = "urn:getBook")
    @WebResult(name = "getBookReturn",
        targetNamespace = "http://ws.sosnoski.com/library/wsdl")
    @RequestWrapper(localName = "getBook",
        targetNamespace = "http://ws.sosnoski.com/library/wsdl",
        className = "com.sosnoski.ws.library.jaxws.GetBook")
    @ResponseWrapper(localName = "getBookResponse",
        targetNamespace = "http://ws.sosnoski.com/library/wsdl",
        className = "com.sosnoski.ws.library.jaxws.GetBookResponse")
    public BookInformation getBook(
        @WebParam(name = "isbn", targetNamespace = "http://ws.sosnoski.com/library/wsdl")
        String isbn);

    @WebMethod(action = "urn:getBooksByType")
    @WebResult(name = "getBooksByTypeReturn",
        targetNamespace = "http://ws.sosnoski.com/library/wsdl")
    @RequestWrapper(localName = "getBooksByType",
        targetNamespace = "http://ws.sosnoski.com/library/wsdl",
        className = "com.sosnoski.ws.library.jaxws.GetBooksByType")
    @ResponseWrapper(localName = "getBooksByTypeResponse",
        targetNamespace = "http://ws.sosnoski.com/library/wsdl",
        className = "com.sosnoski.ws.library.jaxws.GetBooksByTypeResponse")
    public List<BookInformation> getBooksByType(
        @WebParam(name = "type", targetNamespace = "http://ws.sosnoski.com/library/wsdl")
        String type);

    @WebMethod(action = "urn:getTypes")
    @WebResult(name = "getTypesReturn",
        targetNamespace = "http://ws.sosnoski.com/library/wsdl")
    @RequestWrapper(localName = "getTypes",
        targetNamespace = "http://ws.sosnoski.com/library/wsdl",
        className = "com.sosnoski.ws.library.jaxws.GetTypes")
    @ResponseWrapper(localName = "getTypesResponse",
        targetNamespace = "http://ws.sosnoski.com/library/wsdl",
        className = "com.sosnoski.ws.library.jaxws.GetTypesResponse")
    public List<TypeInformation> getTypes();

    @WebMethod(action = "urn:addBook")
    @WebResult(name = "addBookReturn",
        targetNamespace = "http://ws.sosnoski.com/library/wsdl")
    @RequestWrapper(localName = "addBook",
        targetNamespace = "http://ws.sosnoski.com/library/wsdl",
        className = "com.sosnoski.ws.library.jaxws.AddBook")
    @ResponseWrapper(localName = "addBookResponse",
        targetNamespace = "http://ws.sosnoski.com/library/wsdl",
        className = "com.sosnoski.ws.library.jaxws.AddBookResponse")
    public boolean addBook(
        @WebParam(name = "type", targetNamespace = "http://ws.sosnoski.com/library/wsdl")
        String type,
        @WebParam(name = "isbn", targetNamespace = "http://ws.sosnoski.com/library/wsdl")
        String isbn,
        @WebParam(name = "author",
            targetNamespace = "http://ws.sosnoski.com/library/wsdl")
        List<String> author,
        @WebParam(name = "title", targetNamespace = "http://ws.sosnoski.com/library/wsdl")
        String title)
        throws AddDuplicateFault
    ;
}

Утилита WsImport анализирует описание упакованной версии интерфейса в WSDL и генерирует его неупакованный вариант. В итоге методы интерфейса, показанного в листинге 5, принимают набор аргументов, соответствующих входным параметрам операций сервиса, и возвращают результат конкретного типа, в отличие от предыдущей версии, в которой использовались объекты-обертки. При этом обертки по-прежнему применяются JAX-WS, которая упаковывает и распаковывает сообщения прозрачным для разработчиков образом.

Исходный код демонстрационного приложения включает реализацию сервиса и тестового клиента. Для запуска необходимо откорректировать файл build.properties, указав корректные пути к каталогам, в которых установлены Axis2 и JAX-WS (см. Ресурсы). После этого перейдите в каталог jaxws и выполните команду ant из командной строки, чтобы сгенерировать классы JAX-WS, скомпилировать приложение и создать файл JAR для развертывания на сервере. Затем скопируйте сгенерированный JAR в каталог WEB-INF/servicejars внутри директории, в которой установлен сервер Axis2, и запустите клиентское приложение командой ant run.

Использование JAX-WS в клиентском приложении

Полный вариант тестового клиентского кода показан в листинге 6. Если сравнить его с содержимым листинга 2, то бросится в глаза разница между упакованным и неупакованным вариантами интерфейсов, причем, как видно, последний значительно удобнее для разработчика.

Листинг 6. Клиентский код, использующий
public class WebServiceClient
{
    public static void main(String[] args) throws Exception {
        
        // проверка наличия параметров командной строки
        if (args.length < 3) {
            System.out.println("Usage:\n  java " +
                "com.sosnoski.ws.library.jaxws.WebServiceClient host port path");
            System.exit(1);
        }
        
        // создание клиентской заглушки
        JaxwsLibrary service = new JaxwsLibrary();
        Library stub = service.getLibrary();
        
        // задание адреса сервиса
        String target = "http://" + args[0] + ":" + args[1] + args[2];
        System.out.println("Connecting to " + target);
        BindingProvider provider = (BindingProvider)stub;
        provider.getRequestContext().
            put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, target);
        
        // получение информации о книге
        String isbn = "0061020052";
        BookInformation book = stub.getBook(isbn);
        if (book == null) {
            System.out.println("No book found with ISBN '" + isbn + '\'');
        } else {
            System.out.println("Retrieved '" + book.getTitle() + '\'');
        }
        
        // получение списка типов книг
        List<TypeInformation> types = stub.getTypes();
        System.out.println("Retrieved " + types.size() + " types:");
        for (int i = 0; i < types.size(); i++) {
            TypeInformation type = types.get(i);
            System.out.println(" '" + type.getName() + "' with " +
                type.getCount() + " books");
        }
        
        // добавление книги
        String title = "The Dragon Never Sleeps";
        isbn = "0445203498";
        try {
            List<String> authors = new ArrayList<String>();
            authors.add("Cook, Glen");
            stub.addBook("scifi", isbn, authors, title);
            System.out.println("Added '" + title + '\'');
            title = "This Should Not Work";
            stub.addBook("scifi", isbn, authors, title);
            System.out.println("Added duplicate book - should not happen!");
        } catch (AddDuplicateFault e) {
            System.out.println("Failed adding '" + title +
                "' with ISBN '" + isbn + "' - matches existing title '" +
                e.getFaultInfo().getBook().getTitle() + '\'');
        }
        
        // получение всех книг указанного типа
        List<BookInformation> books = stub.getBooksByType("scifi");
        System.out.println("Retrieved " + books.size() + " books of type 'scifi':");
        for (int i = 0; i < books.size(); i++) {
            System.out.println(" '" + books.get(i).getTitle() + '\'');
        }
    }
}

Как правило, подразумевается, что клиентские классы JAX-WS имеют доступ к WSDL сервиса на этапе выполнения и могут его использовать для инициализиации связи с сервером. Если вы уверены в том, что WSDL-описание сервиса всегда будет доступно, а сам сервис всегда будет находиться по одному и тому же адресу, то можно просто передать URL документа WSDL в WsImport, в результате чего ссылка будет жестко задана в коде сгенерированных классов. Однако, в серьезных проектах следует хранить локальную копию файла WSDL и динамически менять адрес сервиса на этапе выполнения, если он отличается от указанного в описании. Подобный подход применяется в файле сборки демонстрационного приложения, а в листинге 6 жирным шрифтом выделен фрагмент кода, показывающий, как можно изменить адрес сервиса при выполнении приложения, не модифицируя описание в WSDL.

Использование JAX-WS на стороне сервера

Серверный код, использующий классы JAX-WS, показан в листинге 7. Выделенная жирным шрифтом аннотация @WebService  содержит информацию, сопоставляющую класс реализации с интерфейсом Web-сервиса. Она позволяет переопределять настройки, заданные при помощи аналогичной аннотации в сгенерированном интерфейсе сервиса, который был показан в листинге 5. В данном случае в аннотации указаны имя и порт сервиса, а также расположение документа WSDL, описывающего сервис (Axis2 позволяет задавать его в виде либо абсолютного URL, либо пути относительно корневого каталога classpath).

Листинг 7. Серверный код, использующий JAX-WS
@javax.jws.WebService(endpointInterface="com.sosnoski.ws.library.jaxws.Library",
portName="library", targetNamespace="http://ws.sosnoski.com/library/wsdl",
wsdlLocation="com/sosnoski/ws/library/jaxws/library.wsdl", serviceName="JaxwsLibrary")
public class JaxwsLibraryImpl implements Library
{
    private final BookServer m_server;
    
    public JaxwsLibraryImpl() {
        m_server = new BookServer();
    }
    
    public boolean addBook(String type, String isbn, List<String> author, String title)
        throws AddDuplicateFault {
        BookInformation prior = m_server.getBook(isbn);
        if (prior == null) {
            BookInformation book = new BookInformation();
            book.getAuthor().addAll(author);
            book.setIsbn(isbn);
            book.setTitle(title);
            book.setType(type);
            return m_server.addBook(book);
        } else {
            AddDuplicate ad = new AddDuplicate();
            ad.setBook(prior);
            AddDuplicateFault e =
                new AddDuplicateFault("Book already present with matching ISBN", ad);
            throw e;
        }
    }

    public BookInformation getBook(String isbn) {
        return m_server.getBook(isbn);
    }

    public List<BookInformation> getBooksByType(String type) {
        return m_server.getBooksByType(type);
    }

    public List<TypeInformation> getTypes() {
        return m_server.getTypes();
    }
}

Остальная часть листинга 7 представляет собой неупакованный вариант реализации сервиса, которая была продемонстрирована в разделе об использовании JAXB (листинг 3).

Проблемы, связанные с использованием JAX-WS в Axis2

Axis2 предоставляет хорошую поддержку базовых аспектов JAX-WS, которая, однако, имеет ряд ограничений. Наиболее существенным из них является отсутствие поддержки WS-Security и других продвинутых технологий Web-сервисов (надо заметить, что серверы приложений, созданные на основе Axis2, могут предоставлять собственные средства для конфигурирования и использования WS-Security). Это представляет собой серьезную проблему для корпоративных систем, поэтому на данный момент такие преимущества, как упрощение конфигурирования Web-сервисов, не являются достаточным основанием для использования JAX-WS в Axis2.


Заканчивая с Axis2

В этой статье рассказывалось об основах использования стандартных Java-технологий JAXB 2.x и JAX-WS 2.x в Axis2. Поддержка JAXB в Axis2 имеет определенные ограничения, однако, при работе с простыми схемами, которые не требуют сложного конфигурирования, JAXB можно рассматривать в качестве серьезной альтернативы другим технологиям для связывания с данными, которые можно использовать в Axis2. В свою очередь поддержка JAX-WS имеет более существенные ограничения и на данный момент этот подход имеет смысл применять только при создании простых сервисов, не требующих функциональности WS-Security и подобных технологий.

Вплоть до этой статьи в данной серии основное внимание уделялось инфраструктуре Apache Axis2, но рассмотрение таких технологий, как JAXB и JAX-WS служит прекрасным поводом обратиться к другим инфраструктурам Web-сервисов Java, поддерживающим эти стандарты. Следующая статья серии будет посвящена проекту Metro Web Services, разрабатываемому Sun совместно с эталонными реализациями JAXB и JAX-WS. Кроме того, будут более детально рассмотрены возможности и аспекты использования JAX-WS.


Загрузка

ОписаниеИмяРазмер
Исходный код примеров к статьеj-jws8.zip57 KБ

Ресурсы

Научиться

  • Оригинал статьи: "Java Web services: JAXB and JAX-WS in Axis2" (Деннис Сосноски, developerWorks, сентябрь 2009 г.). (EN)
  • Посетите сайт Apache Axis2, на котором содержится информация о разработке и состоянии проекта, а также ссылки на списки рассылки, файлы для загрузки и систему отслеживания задач. (EN)
  • Прочитайте статью "Web-сервисы Java, часть 3: связывание с данными в Axis2" (Деннис Сосноски, developerWorks, июль 2007 г.), в которой рассматриваются основные технологии для связывания с данными, поддерживаемые в Axis2. Исходный код к этой статье служит в качестве основы для примеров в данной серии.
  • Посетите домашнюю страницу эталонной реализации JAXB. (EN)
  • Посетите домашнюю страницу эталонной реализации JAX-WS. (EN)
  • Сайт Axis2 Jira содержит описания известных проблем Axis2. Проблемы, относящиеся к технологиям для связывания с данными, в том числе JAXB, находятся в категории компонента databinding, а относящиеся к JAX-WS - в категории компонента jaxws. (EN)
  • Обратитесь к магазину технической литературы, в котором представлены книги на данную и другие темы. (EN)
  • Сотни статей по всем аспектам программирования на Java можно найти на сайте developerWorks, в разделе Технология Java.

Получить продукты и технологии

Обсудить

Комментарии

developerWorks: Войти

Обязательные поля отмечены звездочкой (*).


Нужен IBM ID?
Забыли Ваш IBM ID?


Забыли Ваш пароль?
Изменить пароль

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Профиль создается, когда вы первый раз заходите в developerWorks. Информация в вашем профиле (имя, страна / регион, название компании) отображается для всех пользователей и будет сопровождать любой опубликованный вами контент пока вы специально не укажите скрыть название вашей компании. Вы можете обновить ваш IBM аккаунт в любое время.

Вся введенная информация защищена.

Выберите имя, которое будет отображаться на экране



При первом входе в developerWorks для Вас будет создан профиль и Вам нужно будет выбрать Отображаемое имя. Оно будет выводиться рядом с контентом, опубликованным Вами в developerWorks.

Отображаемое имя должно иметь длину от 3 символов до 31 символа. Ваше Имя в системе должно быть уникальным. В качестве имени по соображениям приватности нельзя использовать контактный e-mail.

Обязательные поля отмечены звездочкой (*).

(Отображаемое имя должно иметь длину от 3 символов до 31 символа.)

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Вся введенная информация защищена.


  • Bluemix

    Узнайте больше информации о платформе IBM Bluemix, создавайте приложения, используя готовые решения!

  • developerWorks Premium

    Эксклюзивные инструменты для построения вашего приложения. Узнать больше.

  • Библиотека документов

    Более трех тысяч статей, обзоров, руководств и других полезных материалов.

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Технология Java, SOA и web-сервисы, Open source
ArticleID=468503
ArticleTitle=Web-сервисы Java: JAXB и JAX-WS в Axis2
publish-date=02162010