Разработанное вами приложение CXF-POJO приводит к очень тесной связи между клиентом и сервером. Предоставление прямого доступа к интерфейсу службы также может создавать серьезные угрозы безопасности. Таким образом, обычно требуется разделение между клиентом и сервером, что достигается с помощью WSDL (языка описания веб-служб).
Мы пишем интерфейс веб-службы в документе WSDL, основанном на XML. Мы будем использовать инструмент для привязки этого WSDL к интерфейсам Apache CXF, которые затем реализуются и используются нашими клиентскими и серверными приложениями. Для обеспечения развязки предпочтительным является запуск с WSDL. Для этого вам нужно сначала выучить новый язык — WSDL. Написание WSDL требует осторожного подхода, и было бы лучше, если бы вы смогли немного разобраться в этом, прежде чем начать работать над ним.
В этом уроке мы начнем с определения интерфейса веб-службы в документе WSDL. Мы узнаем, как использовать CXF для создания серверных и клиентских приложений, начиная с WSDL. Мы сделаем приложение простым, чтобы сосредоточиться на использовании CXF. После создания серверного приложения мы опубликуем его по желаемому URL-адресу, используя встроенный класс CXF.
Сначала давайте опишем WSDL, который мы собираемся использовать.
WSDL для HelloWorld
Веб-сервис, который мы собираемся реализовать, будет иметь один единственный веб-метод, называемый greetings, который принимает строковый параметр, содержащий имя пользователя, и возвращает строковое сообщение вызывающей стороне после добавления сообщения приветствия к имени пользователя. Полный wsdl показан ниже —
//Hello.wsdl <?xml version = "1.0" encoding = "UTF-8"?> <wsdl:definitions xmlns:soap = "http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns = "http://helloworld.tutorialspoint.com/" xmlns:wsdl = "http://schemas.xmlsoap.org/wsdl/" xmlns:xsd = "http://www.w3.org/2001/XMLSchema" name = "HelloWorld" targetNamespace = "http://helloworld.tutorialspoint.com/"> <wsdl:types> <xsd:schema attributeFormDefault = "unqualified" elementFormDefault = "qualified" targetNamespace = "http://helloworld.tutorialspoint.com/"> <xsd:element name = "greetings" type = "tns:greetings"/> <xsd:complexType name = "greetings"> <xsd:sequence> <xsd:element minOccurs = "0" name = "arg0" type = "xsd:string"/> </xsd:sequence> </xsd:complexType> <xsd:element name = "greetingsResponse" type = "tns:greetingsResponse"/> <xsd:complexType name = "greetingsResponse"> <xsd:sequence> <xsd:element minOccurs = "0" name = "return" type = "xsd:string"/> </xsd:sequence> </xsd:complexType> </xsd:schema> </wsdl:types> <wsdl:message name = "greetings"> <wsdl:part element = "tns:greetings" name = "parameters"> </wsdl:part> </wsdl:message> <wsdl:message name = "greetingsResponse"> <wsdl:part element = "tns:greetingsResponse" name = "parameters"> </wsdl:part> </wsdl:message> <wsdl:portType name = "HelloWorldPortType"> <wsdl:operation name = "greetings"> <wsdl:input message = "tns:greetings" name = "greetings"> </wsdl:input> <wsdl:output message = "tns:greetingsResponse" name = "greetingsResponse"> </wsdl:output> </wsdl:operation> </wsdl:portType> <wsdl:binding name = "HelloWorldSoapBinding" type = "tns:HelloWorldPortType"> <soap:binding style = "document" transport = "http://schemas.xmlsoap.org/soap/http"/> <wsdl:operation name = "greetings"> <soap:operation soapAction = "" style = "document"/> <wsdl:input name = "greetings"></wsdl:input> <wsdl:output name = "greetingsResponse"> <soap:body use = "literal"/> </wsdl:output> </wsdl:operation> </wsdl:binding> <wsdl:service name = "HelloWorldService"> <wsdl:port binding = "tns:HelloWorldSoapBinding" name = "HelloWorldPort"> <soap:address location = "http://localhost:9090/HelloServerPort"/> </wsdl:port> </wsdl:service> </wsdl:definitions>
Обратите внимание, что написание синтаксически правильного wsdl всегда было проблемой для разработчиков; Есть много инструментов и онлайн-редакторы доступны для создания WSDL. Эти редакторы запрашивают имена сообщений, которые вы хотите реализовать, а также параметры, которые вы хотите передать в сообщении, и тип возвращаемого сообщения, которое вы хотите, чтобы ваше клиентское приложение получало. Если вы знаете синтаксис wsdl, вы можете вручную написать весь документ или использовать один из редакторов, чтобы создать свой собственный.
В приведенном выше wsdl мы определили одно сообщение под названием приветствия . Сообщение доставляется в службу под названием HelloWorldService, которая работает по адресу http: // localhost: 9090 / HelloServerPort.
Теперь мы приступим к разработке сервера. Перед разработкой сервера нам нужно сгенерировать интерфейс Apache CXF для нашего веб-сервиса. Это должно быть сделано из данного WSDL. Для этого вы используете инструмент под названием wsdl2java .
Плагин wsdl2java
Поскольку мы будем использовать maven для сборки проекта, вам необходимо добавить следующий плагин в файл pom.xml .
<plugins> <plugin> <groupId>org.apache.cxf</groupId> <artifactId>cxf-codegen-plugin</artifactId> <version>3.3.0</version> <executions> <execution> <id>generate-sources</id> <phase>generate-sources</phase> <configuration> <wsdlOptions> <wsdlOption> <wsdl>src/main/resources/hello.wsdl</wsdl> <faultSerialVersionUID> 1 </faultSerialVersionUID> </wsdlOption> </wsdlOptions> </configuration> <goals> <goal>wsdl2java</goal> </goals> </execution> </executions> </plugin> </plugins>
Обратите внимание, что мы указываем расположение файла wsdl как src / main / resources / Hello.wsdl . Вам необходимо убедиться, что вы создали соответствующую структуру каталогов для вашего проекта и добавили ранее указанный файл hello.wsdl в указанную папку.
Плагин wsdl2java скомпилирует этот wsdl и создаст классы Apache CXF в предопределенной папке. Полная структура проекта показана здесь для вашего удобства.
Теперь вы готовы создать сервер, используя сгенерированные классы wsdl2java . Классы, созданные wsdl2java, показаны на рисунке ниже —
Созданный сервисный интерфейс
В списке сгенерированных классов вы должны были заметить, что один из них — это интерфейс Apache CXF — это HelloWorldPortType.java . Изучите этот файл в вашем редакторе кода. Содержимое файла показано здесь для вашей готовой ссылки —
//HelloWorldPortType.java package com.tutorialspoint.helloworld; import javax.jws.WebMethod; import javax.jws.WebParam; import javax.jws.WebResult; import javax.jws.WebService; import javax.xml.bind.annotation.XmlSeeAlso; import javax.xml.ws.RequestWrapper; import javax.xml.ws.ResponseWrapper; /** * This class was generated by Apache CXF 3.3.0 * 2019-02-11T12:05:55.220+05:30 * Generated source version: 3.3.0 * */ @WebService(targetNamespace = "http://helloworld.tutorialspoint.com/", name = "HelloWorldPortType") @XmlSeeAlso({ObjectFactory.class}) public interface HelloWorldPortType { @WebMethod @RequestWrapper(localName = "greetings", targetNamespace = "http://helloworld.tutorialspoint.com/", className = "com.tutorialspoint.helloworld.Greetings") @ResponseWrapper(localName = "greetingsResponse", targetNamespace = "http://helloworld.tutorialspoint.com/", className = "com.tutorialspoint.helloworld.GreetingsResponse") @WebResult(name = "return", targetNamespace = "http://helloworld.tutorialspoint.com/") public java.lang.String greetings( @WebParam(name = "arg0", targetNamespace = "http://helloworld.tutorialspoint.com/") java.lang.String arg0 ); }
Обратите внимание, что интерфейс содержит метод с именем greetings . Это был тип сообщения в нашем wsdl. Инструмент wsdl2java добавил этот метод в сгенерированный интерфейс. Теперь вы можете понять, что какие бы сообщения вы ни писали в своем wsdl, соответствующий интерфейс будет сгенерирован в интерфейсе.
Теперь ваша задача — реализовать все эти методы, соответствующие различным сообщениям, которые вы определили в своем wsdl. Обратите внимание, что в более раннем примере Apache CXF-First мы начали с интерфейса Apache CXF для нашего веб-сервиса. В этом случае интерфейс Apache CXF создается из wsdl.
Реализация интерфейса сервиса
Реализация интерфейса сервиса тривиальна. Полная реализация показана в листинге ниже —
//HelloWorldImpl.java package com.tutorialspoint.helloworld; public class HelloWorldImpl implements HelloWorldPortType { @Override public String greetings(String name) { return ("hi " + name); } }
Код реализует единственный интерфейсный метод, называемый приветствиями . Метод принимает один параметр строкового типа, добавляет к нему сообщение «hi» и возвращает результирующую строку вызывающей стороне.
Далее мы напишем сервер приложения.
Развивающийся сервер
Разработка серверного приложения снова тривиальна. Здесь мы будем использовать предоставленный CXF класс Endpoint для публикации нашего сервиса. Это делается в следующих двух строках кода:
HelloWorldPortType implementor = new HelloWorldImpl(); Endpoint.publish("http://localhost:9090/HelloServerPort", implementor, new LoggingFeature());
Сначала мы создаем объект нашего класса реализации сервиса — HelloWorldImpl . Затем мы передаем эту ссылку в качестве второго параметра методу публикации . Первый параметр — это адрес, по которому публикуется сервис — клиенты будут использовать этот URL для доступа к сервису. Весь исходный текст для серверного приложения приведен здесь —
//Server.java package com.tutorialspoint.helloworld; import javax.xml.ws.Endpoint; import org.apache.cxf.ext.logging.LoggingFeature; public class Server { public static void main(String[] args) throws Exception { HelloWorldPortType implementor = new HelloWorldImpl(); Endpoint.publish("http://localhost:9090/HelloServerPort", implementor, new LoggingFeature()); System.out.println("Server ready..."); Thread.sleep(5 * 60 * 1000); System.out.println("Server exiting"); System.exit(0); } }
Чтобы собрать этот класс сервера, вам нужно добавить профиль сборки в ваш pom.xml . Это показано ниже —
<profile> <id>server</id> <build> <defaultGoal>test</defaultGoal> <plugins> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>exec-maven-plugin</artifactId> <version>1.6.0</version> <executions> <execution> <phase>test</phase> <goals> <goal>java</goal> </goals> <configuration> <mainClass> com.tutorialspoint.helloworld.Server </mainClass> </configuration> </execution> </executions> </plugin> </plugins> </build> <dependencies> <dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-rt-transports-http-jetty</artifactId> <version>3.3.0</version> </dependency> </dependencies> </profile>
Обратите внимание, что полное имя класса Server указывается в конфигурации. Кроме того, тег зависимостей указывает, что мы будем использовать встроенный веб-сервер Jetty для развертывания нашего серверного приложения.
Развертывание Сервера
Наконец, чтобы развернуть серверное приложение, вам нужно будет сделать еще одну модификацию в pom.xml, чтобы настроить приложение как веб-приложение. Код, который вам нужно добавить в ваш pom.xml , приведен ниже —
<defaultGoal>install</defaultGoal> <pluginManagement> <plugins> <plugin> <artifactId>maven-war-plugin</artifactId> <version>3.2.2</version> <configuration> <webXml>src/main/webapp/WEB-INF/web.xml</webXml> <webResources> <resource> <directory>src/main/resources</directory> <targetPath>WEB-INF</targetPath> <includes> <include>*.wsdl</include> </includes> </resource> </webResources> </configuration> </plugin> </plugins> </pluginManagement>
Перед развертыванием приложения необходимо добавить еще два файла в ваш проект. Это показано на скриншоте ниже —
Эти файлы являются стандартными файлами CXF, которые определяют отображение для CXFServlet . Код в файле web.xml показан здесь для вашего быстрого ознакомления —
//cxf-servlet.xml <web-app xmlns = "http://java.sun.com/xml/ns/javaee" xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" version="2.5" xsi:schemaLocation = "http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> <display-name>cxf</display-name> <servlet> <description>Apache CXF Endpoint</description> <display-name>cxf</display-name> <servlet-name>cxf</servlet-name> <servlet-class> org.apache.cxf.transport.servlet.CXFServlet </servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>cxf</servlet-name> <url-pattern>/services/*</url-pattern> </servlet-mapping> <session-config> <session-timeout>60</session-timeout> </session-config> </web-app>
В файле cxf-servlet.xml вы объявляете свойства конечной точки вашего сервиса. Это показано во фрагменте кода ниже —
<beans ...> <jaxws:endpoint xmlns:helloworld = "http://tutorialspoint.com/" id="helloHTTP" address = "http://localhost:9090/HelloServerPort" serviceName = "helloworld:HelloServiceService" endpointName = "helloworld:HelloServicePort"> </jaxws:endpoint> </beans>
Здесь мы определяем идентификатор нашей конечной точки службы, адрес, по которому служба будет доступна, имя службы и имя конечной точки. Теперь вы понимаете, как ваш сервис маршрутизируется и обрабатывается сервлетом CXF.
Финал pom.xml
Pom.xml включает в себя еще несколько зависимостей. Вместо того, чтобы описывать все зависимости, мы включили финальную версию pom.xml ниже —
<?xml version="1.0" encoding = "UTF-8"?> <project xmlns = "http://maven.apache.org/POM/4.0.0" xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation = "http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.tutorialspoint</groupId> <artifactId>cxf-wsdl</artifactId> <version>1.0</version> <packaging>jar</packaging> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> </properties> <build> <defaultGoal>install</defaultGoal> <pluginManagement> <plugins> <plugin> <artifactId>maven-war-plugin</artifactId> <version>3.2.2</version> <configuration> <webXml>src/main/webapp/WEB-INF/web.xml</webXml> <webResources> <resource> <directory>src/main/resources</directory> <targetPath>WEB-INF</targetPath> <includes> <include>*.wsdl</include> </includes> </resource> </webResources> </configuration> </plugin> </plugins> </pluginManagement> <plugins> <plugin> <groupId>org.apache.cxf</groupId> <artifactId>cxf-codegen-plugin</artifactId> <version>3.3.0</version> <executions> <execution> <id>generate-sources</id> <phase>generate-sources</phase> <configuration> <wsdlOptions> <wsdlOption> <wsdl>src/main/resources/Hello.wsdl</wsdl> <faultSerialVersionUID>1</faultSerialVersionUID> </wsdlOption> </wsdlOptions> </configuration> <goals> <goal>wsdl2java</goal> </goals> </execution> </executions> </plugin> </plugins> </build> <profiles> <profile> <id>server</id> <build> <defaultGoal>test</defaultGoal> <plugins> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>exec-maven-plugin</artifactId> <version>1.6.0</version> <executions> <execution> <phase>test</phase> <goals> <goal>java</goal> </goals> <configuration> <mainClass> com.tutorialspoint.helloworld.Server </mainClass> </configuration> </execution> </executions> </plugin> </plugins> </build> <dependencies> <dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-rt-transports-http-jetty</artifactId> <version>3.3.0</version> </dependency> </dependencies> </profile> <profile> <id>client</id> <build> <defaultGoal>test</defaultGoal> <plugins> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>exec-maven-plugin</artifactId> <executions> <execution> <phase>test</phase> <goals> <goal>java</goal> </goals> <configuration> <mainClass> com.tutorialspoint.helloworld.Client </mainClass> </configuration> </execution> </executions> </plugin> </plugins> </build> </profile> </profiles> <dependencies> <dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-rt-frontend-jaxws</artifactId> <version>3.3.0</version> </dependency> <dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-rt-transports-http</artifactId> <version>3.3.0</version> </dependency> <dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-rt-management</artifactId> <version>3.3.0</version> </dependency> <dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-rt-features-metrics</artifactId> <version>3.3.0</version> </dependency> <dependency> <groupId>org.apache.cxf.xjc-utils</groupId> <artifactId>cxf-xjc-runtime</artifactId> <version>3.3.0</version> </dependency> <dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-rt-features-logging</artifactId> <version>3.3.0</version> </dependency> <dependency> <groupId>org.codehaus.mojo</groupId> <artifactId>exec-maven-plugin</artifactId> <version>1.6.0</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.8.0-beta2</version> </dependency> <dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-rt-transports-http-jetty</artifactId> <version>3.3.0</version> </dependency> </dependencies> </project>
Обратите внимание, что он также включает профиль для построения клиента, который мы вскоре изучим в последующих разделах.
Запуск службы HelloWorld
Теперь вы готовы запустить веб-приложение. В командном окне запустите скрипт сборки, используя следующую команду.
mvn clean install
Это сгенерирует соответствующие классы Apache CXF из вашего wsdl, скомпилирует ваши классы Apache CXF, развернет сервер на встроенном сервере Jetty и запустит ваше приложение.
Вы увидите следующее сообщение на консоли —
INFO: Setting the server's publish address to be http://localhost:9090/HelloServerPort Server ready...
Как и раньше, вы можете протестировать сервер, открыв URL-адрес сервера в браузере.
Поскольку мы не указали никакой операции, наше приложение возвращает браузеру только сообщение об ошибке. Теперь попробуйте добавить ? Wsdl в ваш URL, и вы увидите следующий результат:
Итак, наше серверное приложение работает, как и ожидалось. Вы можете использовать клиент SOAP, такой как Postman, описанный ранее, для дальнейшего тестирования вашего сервиса.
Следующая часть этого урока — написать клиент, который использует наш сервис.
Развивающийся клиент
Написание клиента в приложении CXF так же важно, как написание сервера. Вот полный код для клиента, который, по сути, состоит только из трех строк, остальные строки просто печатают служебную информацию для пользователя.
//Client.java package com.tutorialspoint.helloworld; public class Client { public static void main(String[] args) throws Exception { //Create the service client with its default wsdlurl HelloWorldService helloServiceService = new HelloWorldService(); System.out.println("service: " + helloServiceService.getServiceName()); System.out.println("wsdl location: " + helloServiceService.getWSDLDocumentLocation()); HelloWorldPortType helloService = helloServiceService.getHelloWorldPort(); System.out.println(helloService.greetings (System.getProperty("user.name"))); } }
Здесь мы просто создаем экземпляр нашего сервиса HelloWorldService , получаем его порт, вызывая метод getHelloWorldPort , а затем передаем ему приветственное сообщение. Запустите клиент, и вы увидите следующий вывод —
service: {http://helloworld.tutorialspoint.com/}HelloWorldService wsdl location: file:/Users/drsarang/Desktop/tutorialpoint/cxf- wsdl/src/main/resources/Hello.wsdl hi drsarang
До сих пор вы узнали, как использовать CXF с архитектурами Apache CXF-First и WSDL-First. В подходе Apache CXF-First для создания сервера вы использовали POJO с классом ServerFactoryBean из библиотек CXF. Для создания клиента вы использовали класс ClientProxyFactoryBean из библиотеки CXF. В подходе WSDL-First вы использовали класс Endpoint для публикации сервиса по желаемому URL и указанному разработчику. Теперь вы можете расширить эти методы для интеграции различных протоколов и транспортов.