Статьи

Создание и развертывание веб-службы JAX-WS на Tomcat 6

Несколько лет назад мне пришлось предоставить оболочку для удаленного сервиса EJB 3.0, чтобы придумать простой проект веб-сервиса, который будет развернут через Tomcat и доступ к нему будет простым способом http из-за некоторых проблем с доступностью. Теперь, когда я не могу раскрыть фактическое требование, которое я реализовал в то время, здесь я представляю простой демонстрационный вид сервиса со следующей подписью.

public AccountDetails getAccountDetails (String accountNo, токен SecurityToken);

Сервис вернет данные учетной записи определенного номера учетной записи, при условии, что токен действителен (сгенерирован с использованием некоторого модуля безопасности приложения). В двух словах, клиент запросит токен из модуля безопасности и затем вызовет этот метод. Служба проверит токен, чтобы узнать, может ли вызывающая сторона вызвать метод или нет? В общем, вы должны использовать обработчики (перехватчики сообщений, которые можно легко подключить к среде выполнения JAX-WS для дополнительной обработки входящих и исходящих сообщений) для проверки содержимого, освобождая класс реализации от этих издержек, это всего лишь примерное упражнение поэтому наш класс реализации также проверит токен безопасности. Звучит хорошо .. давайте двигаться дальше.

Библиотеки, которые мы собираемся использовать, включают в себя JAXB и JAX-WS, поскольку обе они имеют разумные значения по умолчанию, количество аннотаций может быть сведено к минимуму. Также, на мой взгляд, всегда лучше разрабатывать WSDL и схемы вручную, чтобы гарантировать, что контракт на обслуживание определен надлежащим образом, а также что схема может быть повторно использована (другими службами) и расширена при необходимости. Я не предпочитаю использовать аннотации для автоматического создания WSDL и схемы во время выполнения, поэтому давайте начнем с определения, я использую встроенную схему, поскольку здесь у нас очень простое требование, хотя у нас должны быть определения схемы в отдельных файлах xsd.

accounts.wsdl:

<?xml version="1.0" encoding="UTF-8"?><definitions name="AccountDetails"  targetNamespace="http://gognamunish.com/accounts"         xmlns:tns="http://gognamunish.com/accounts"          xmlns="http://schemas.xmlsoap.org/wsdl/"         xmlns:xsd="http://www.w3.org/2001/XMLSchema"          xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"><types><xsd:schema xmlns="http://www.w3.org/2001/XMLSchema"elementFormDefault="qualified" targetNamespace="http://gognamunish.com/accounts"><element name="geAccountDetails_INTO" type="tns:GeAccountDetails_INTO" /><element name="accountDetails_TO" type="tns:AccountDetails_TO" /><element name="accountDetailsFault" type="tns:AccountDetailsFault" /><element name="securityToken" type="tns:SecurityToken" /><complexType name="GeAccountDetails_INTO"><sequence><element name="accountNo" type="xsd:string" /></sequence></complexType><complexType name="AccountDetails_TO"><sequence><element name="accNo" type="xsd:string" /><element name="accType" type="xsd:string" /><element name="balance" type="xsd:decimal" /></sequence></complexType><complexType name="SecurityToken"><sequence><element name="token" type="xsd:string" /><element name="validTill" type="xsd:date" /></sequence></complexType><complexType name="AccountDetailsFault"><sequence><element name="faultInfo" type="xsd:string" /><element name="message" type="xsd:string" /></sequence></complexType></xsd:schema></types><!--  messages format --><message name="accountDetailsRequest"><part name="parameters" element="tns:geAccountDetails_INTO" /><part name="request_header" element="tns:securityToken"/></message><message name="accountDetailsResponse"><part name="parameters" element="tns:accountDetails_TO" /></message><message name="accountDetailsFault"><part name="faultInfo" element="tns:accountDetailsFault" /></message><!-- define getAccountDetails operation here --><portType name="AccountDetailsPortType"><operation name="getAccountDetails"><input message="tns:accountDetailsRequest" /><output message="tns:accountDetailsResponse" /><fault message="tns:accountDetailsFault" name="accountDetailsFault"/></operation></portType><!-- bind the operations --><binding name="AccountDetailsBinding" type="tns:AccountDetailsPortType"><soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document" /><operation name="getAccountDetails"><soap:operation soapAction="getAccountDetails" /><input><soap:body use="literal" parts="parameters"/><soap:header use="literal" part="request_header" message="tns:accountDetailsRequest"></soap:header></input><output><soap:body use="literal" /></output><fault name="accountDetailsFault"><soap:fault name="accountDetailsFault" use="literal" /></fault></operation></binding><!-- name and location of the service --><service name="AccountDetailsService"><port name="AccountDetailsPort" binding="tns:AccountDetailsBinding"><soap:address location="http://localhost:8080/account/details" /></port></service></definitions>

Я не буду объяснять входы и выходы этого wsdl, в двух словах, он определяет одну операцию ‘getAccountDetails’, которая принимает accountNo и возвращает обратно данные учетной записи, такие как тип учетной записи, баланс и т. Д. Обратите внимание, что я также добавил заголовок безопасности токен, который будет проверять вызывающего (оставлен для реализации сервиса).

Теперь, когда мы закончили с нашими wsdl и схемой, давайте сгенерируем переносимые артефакты из нашего определения сервиса. JAX-WS включает в себя инструмент, который может сделать это для нас, мы будем использовать этот инструмент для генерации переносимых артефактов, таких как интерфейс Service Endpoint Interface (SEI), классы Service и Exception. Эти артефакты могут быть упакованы в файл WAR с WSDL и документами схемы наряду с реализацией конечной точки, которая будет развернута.

Чтобы сгенерировать артефакты, мы запускаем следующую команду:

wsimport C: \ devel \ workspace \ webservice \ WebContent \ WEB-INF \ wsdl \ accounts.wsdl -p com.mg.ws -keep -Xnocompile

Это создаст следующие артефакты в пакете com.mg.ws.

  • AccountDetails.java
  • AccountDetailsFault.java
  • AccountDetailsFault_Exception.java
  • AccountDetailsPortType.java
  • AccountDetailsTO.java
  • GeAccountDetailsINTO.java
  • ObjectFactory.java
  • package-info.java
  • SecurityToken.java

Теперь, когда у нас есть все артефакты, которые мы готовы реализовать, мы должны реализовать интерфейс AccountDetailsPortType, поэтому давайте сделаем это.

Вот наш фиктивный класс реализации:

package com.mg.ws.impl;import java.math.BigDecimal;import javax.jws.WebService;import com.mg.ws.AccountDetailsFault;import com.mg.ws.AccountDetailsFault_Exception;import com.mg.ws.AccountDetailsPortType;import com.mg.ws.AccountDetailsTO;import com.mg.ws.GeAccountDetailsINTO;import com.mg.ws.SecurityToken;@WebService(name = "AccountDetailsService",       portName = "AccountDetailsPort",       endpointInterface = "com.mg.ws.AccountDetailsPortType",       wsdlLocation = "WEB-INF/wsdl/accounts.wsdl",      targetNamespace="http://gognamunish.com/accounts")     public class AccountDetailsServiceImpl implements AccountDetailsPortType {public AccountDetailsTO getAccountDetails(GeAccountDetailsINTO parameters,   SecurityToken requestHeader) throws AccountDetailsFault_Exception {  AccountDetailsTO detailsTO = new AccountDetailsTO();  // validate token   validateToken(requestHeader);  // populate response  detailsTO = getDetailsFromSomewhere (parameters.getAccountNo());  return detailsTO; } private AccountDetailsTO getDetailsFromSomewhere(String accountNo) throws AccountDetailsFault_Exception {  if(accountNo == null || accountNo.trim().length()==0){   AccountDetailsFault faultInfo = new AccountDetailsFault();   faultInfo.setFaultInfo("missing account number");   faultInfo.setMessage("account number is required field");   throw new AccountDetailsFault_Exception("account no missing", faultInfo);  }  AccountDetailsTO detailsTO = new AccountDetailsTO();  detailsTO.setAccNo(accountNo);  detailsTO.setAccType("SAVING");  detailsTO.setBalance(new BigDecimal(10000));  return detailsTO; } private void validateToken(SecurityToken requestHeader) throws AccountDetailsFault_Exception {    if ("83711070".equals(requestHeader.getToken()) && requestHeader.getValidTill() != null){   System.out.println("token processed successfully...");  } else {   AccountDetailsFault faultInfo = new AccountDetailsFault();   faultInfo.setFaultInfo("Header token Invalid");   faultInfo.setMessage("can't help");   throw new AccountDetailsFault_Exception("invalid token", faultInfo);  } }}

Это просто фиктивная реализация только для иллюстрации. Теперь, когда реализация нашего сервиса завершена, мы приступаем к упаковке этого в войну и разворачиваем его на tomcat.

Теперь мы создаем стандартный файл web.xml, который определяет WSServletContextListener, WSServlet и структуру веб-проекта.

<?xml version="1.0"?> <!DOCTYPE web-app PUBLIC   "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"   "http://java.sun.com/dtd/web-app_2_3.dtd"><web-app><listener><listener-class>com.sun.xml.ws.transport.http.servlet.WSServletContextListener        </listener-class></listener><servlet><servlet-name>AccountDetailsService</servlet-name><servlet-class>com.sun.xml.ws.transport.http.servlet.WSServlet        </servlet-class><load-on-startup>1</load-on-startup></servlet><servlet-mapping><servlet-name>AccountDetailsService</servlet-name><url-pattern>/details</url-pattern></servlet-mapping></web-app>

Далее мы создаем sun-jaxws.xml, определяющий класс реализации веб-сервиса.

<?xml version="1.0" encoding="UTF-8"?><endpoints  xmlns="http://java.sun.com/xml/ns/jax-ws/ri/runtime"  version="2.0">  <endpoint name="AccountDetailsServiceEndPoint"    service ="{http://gognamunish.com/accounts}AccountDetailsService"      port="{http://gognamunish.com/accounts}AccountDetailsPort"      implementation="com.mg.ws.impl.AccountDetailsServiceImpl"      url-pattern="/details"      wsdl="WEB-INF/wsdl/accounts.wsdl"/></endpoints>

Этот файл необходим независимо от того, публикуем ли мы наш веб-сервис на tomcat, glassfish или любом другом сервере. Хорошо, пока все хорошо, давайте создадим наше приложение и развернем его на tomcat, вот скрипт ant.

<project name="webservice" basedir="../" default="deploy"><!-- Project settings --><property name="project.distname" value="account" /><!-- Local system paths --><property file="${basedir}/ant/build.properties" /><property name="webroot.dir" value="${basedir}/WebContent" /><property name="webinf.dir" value="${webroot.dir}/WEB-INF" /><property name="build.dir" value="build" /><path id="compile.classpath"><!-- classpath for Jax WS related stuff --><pathelement path="${webinf.dir}/lib/activation.jar" /><pathelement path="${webinf.dir}/lib/jaxb-api.jar" /><pathelement path="${webinf.dir}/lib/jaxb-impl.jar" /><pathelement path="${webinf.dir}/lib/jaxp-api.jar" /><pathelement path="${webinf.dir}/lib/jaxws-api.jar" /><pathelement path="${webinf.dir}/lib/jaxws-rt.jar" /><pathelement path="${webinf.dir}/lib/jsr173_api.jar" /><pathelement path="${webinf.dir}/lib/jsr181_api.jar" /><pathelement path="${webinf.dir}/lib/resolver.jar" /><pathelement path="${webinf.dir}/lib/saaj-api.jar" /><pathelement path="${webinf.dir}/lib/saaj-impl.jar" /><pathelement path="${webinf.dir}/lib/sjsxp.jar" /><pathelement path="${webinf.dir}/lib/stax-ex.jar" /><pathelement path="${webinf.dir}/lib/streambuffer.jar" /><pathelement path="${webinf.dir}/classes" /><pathelement path="${classpath.external}" /><pathelement path="${classpath}" /></path><!-- define your folder for deployment --><property name="deploy.dir" value="deploy" /><!-- Local system paths --><property file="${basedir}/ant/build.properties" /><property name="webroot.dir" value="${basedir}/WebContent" /><property name="webinf.dir" value="${webroot.dir}/WEB-INF" /><property name="build.dir" value="build" /><!-- Check timestamp on files --><target name="prepare"><tstamp /></target><!-- Copy any resource or configuration files --><target name="resources"><copy todir="${webinf.dir}/classes" includeEmptyDirs="no"><fileset dir="JavaSource"><patternset><include name="**/*.conf" /><include name="**/*.properties" /><include name="**/*.xml" /></patternset></fileset></copy></target><!-- Normal build of application --><target name="compile" depends="prepare,resources"><javac srcdir="JavaSource" destdir="${webinf.dir}/classes"><classpath refid="compile.classpath" /></javac></target><!-- Remove classes directory for clean build --><target name="clean" description="Prepare for clean build"><delete dir="${webinf.dir}/classes" /><mkdir dir="${webinf.dir}/classes" /></target><!-- Build entire project --><target name="build" depends="prepare,compile" /><target name="rebuild" depends="clean,prepare,compile" /><!-- Create binary distribution --><target name="war" depends="build"><mkdir dir="${build.dir}" /><war basedir="${webroot.dir}" warfile="${build.dir}/${project.distname}.war" webxml="${webinf.dir}/web.xml"><exclude name="WEB-INF/${build.dir}/**" /><exclude name="WEB-INF/src/**" /><exclude name="WEB-INF/web.xml" /></war></target><!-- Create Client --><target name="jar"><jar destfile="${build.dir}/${project.distname}_client.jar" basedir="${webinf.dir}/classes" includes="com/mg/ws/*"/></target><!-- deploy on tomcat --><target name="deploy" depends="war,jar"><delete file="${deploy.dir}/${project.distname}.war" /><delete dir="${deploy.dir}/${project.distname}" /><copy file="${build.dir}/${project.distname}.war" todir="${TOMCAT_HOME}\webapps" /></target></project>

ПРИМЕЧАНИЕ: банки в lib должны быть тщательно выбраны, иначе это сделает вашу жизнь адом.

Теперь давайте протестируем приложение, наведите ваш браузер на http: // localhost: 8080 / account / details. Если вы видите что-то подобное, это означает, что вы успешно развернули сервис.

Теперь давайте протестируем сервис, мы можем использовать клиент, сгенерированный инструментом wsimport:

public static void main(String[] args) throws Exception {  AccountDetails accountDetails = new AccountDetails();  AccountDetailsPortType port = accountDetails.getAccountDetailsPort();  AccountDetailsTO details = port.getAccountDetails(new GeAccountDetailsINTO(), new SecurityToken()); }

Для тех, кто хочет вызвать службу с использованием мыла, они могут использовать инструмент наподобие soapUI (можно загрузить с www.soapui.org ), теперь давайте сделаем несколько вызовов мыла:

case: неверный заголовок безопасности

case: действительный заголовок безопасности

По умолчанию Tomcat не имеет каких-либо зависимостей JAX-WS, поэтому вы должны включить его вручную.

  • Иди сюда
  • Загрузите дистрибутив JAX-WS RI, вы найдете инструмент wsimport в каталоге lib.

Please note for running this project you can just skip this step as I have already done this step for you and included the required libraries in the lib folder of the project.

Я включил проект eclipse для этой демонстрации, пожалуйста, получите его из раздела ресурсов. Для развертывания просто измените build.properties, указав TOMCAT_HOME, и запустите ant build.

Вот и все на данный момент … Пожалуйста, предоставьте ваши ценные комментарии или предложения, и не забудьте проголосовать ?