Web Services
Transkrypt
Web Services
Web Services 1. Podstawy usług sieciowych. ● SOAP, ● WSDL. 2. Usługi sieciowe w JAX-RPC. ● interfejs punktu końcowego, ● korzystanie z usługi z poziomu komponentu EJB, ● programy klienckie, ● narzędzia i deskryptory XML. 3. Usługi sieciowe w JAX-WS. ● adnotacje, ● korzystanie z usługi. 1 SOAP Przykładowy interfejs: public interface TravelAgent { public void makeReservation(int cruiseID, int cabinID, int customerId, double price); } Wywołanie metody poprzez SOAP: <env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:titan="http://www.titan.com/TravelAgent"/> <env:Body> <titan:makeReservation> <cruiseId>23</cruiseId> <cabinId>144</cabinId> <customerId>9393</customerId> <price>5677.88</price> </titan:makeReservation> </env:Body> </env:Envelope> 2 WSDL Aby udostępnić usługę poprzez sieć jako tzw. WebService należy przygotować jej opis w języku WSDL (WebService Description Language). Na jego podstawie klienci korzystający z usługi wiedzą w jaki sposób przesyłać i interpretować odebrane dane. Umożliwia to automatyzację procesu przesyłu danych i pozwala programiście skoncentrować się na logice aplikacji. 3 WSDL – opis usługi WSDL służy do informowania klienta o dostępnych usługach sieciowych: <?xml version="1.0"?> <definitions name="TravelAgent" xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:titan="http://www.titan.com/TravelAgent" targetNamespace="http://www.titan.com/TravelAgent"> <!-- message: opis parametrów wywołania i wyników --> <message name="RequestMessage"> <part name="cruiseId" type="xsd:int" /> <part name="cabinId" type="xsd:int" /> <part name="customerId" type="xsd:int" /> <part name="price" type="xsd:double" /> </message> <message name="ResponseMessage"> <part name="reservationId" type="xsd:string" /> </message> 4 WSDL – opis usługi <!-- portType opisuje interfejs usługi sieciowej --> <portType name="TravelAgent"> <operation name="makeReservation"> <input message="titan:RequestMessage"/> <output message="titan:ResponseMessage"/> </operation> </portType> Element <portType> opisuje dostępne operacje (metody Javy). Operacja posiada wejście <input> i wyjście <output> oraz informacje o błędach (dowolną liczbę) <fault>. EJB 3.0 wspiera dwa typy usług sieciowych: ● request-response – pojedynczy element <input> i <output> oraz ew. <fault>, ● one-way - wyłącznie <input>. 5 WSDL – opis usługi <!-- binding informuje o protokole i kodowaniu --> <binding name="TravelAgentBinding" type="titan:TravelAgent"> <soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/> <operation name="makeReservation"> <soap:operation soapAction="" /> <input> <soap:body use="literal" namespace="http://www.titan.com/TravelAgent"/> </input> <output> <soap:body use="literal" namespace="http://www.titan.com/TravelAgent"/> </output> </operation> </binding> Usługa będzie dostępna poprzez protokół SOAP. 6 WSDL – opis usługi <!-- service: adres usługi --> <service name="TravelAgentService"> <port name="TravelAgentPort" binding="titan:TravelAgentBinding"> <soap:address location="http://www.titan.com/webservices/TravelAgent"/> </port> </service> </definitions> Na zakończenie określamy adres pod którym usługa będzie widoczna w sieci internet. 7 Dostęp do WebServices poprzez JAX-RPC JAX-RPC umożliwia dostęp do usług sieciowych opartych na dowolnych platformach sprzętowych i programowych. 1 program kliencki 2 pośrednik 4 Web Service 3 Pośrednik (proxy) może być generowany dynamicznie na podstawie pobranej z serwera specyfikacji usługi zapisanej w języku WSDL. Do utworzenia tzw. interfejsu punktu końcowego (endpoint interface) wykorzystuje się dane zawarte w elemencie portType. 8 Interfejs punktu końcowego <message name="chargeRequest"> <part name="name" type="xsd:string"/> <part name="number" type="xsd:string"/> <part name="exp-date" type="xsd:dateTime"/> <part name="card-type" type="xsd:string"/> <part name="amount" type="xsd:float"/> </message> <message name="chargeResponse"> <part name="return" type="xsd:int"/> </message> <portType name="Processor"> <operation name="charge"> <input message="tns:chargeRequest"/> <output message="tns:chargeResponse"/> </operation> </portType> package com.charge_it; public interface Processor extends java.rmi.Remote{ public int charge(String name, String number, java.util.Calendar expDate, String cardType, float amount) throws java.rmi.RemoteException; } 9 Adres usługi <service name="ProcessorService"> <port name="ProcessorPort" binding="tns:ProcessorSoapBinding"> <soap:address location="http://www.charge-it.com/ProcessorService"/> </port> </service> Adres usługi jest podany w elemencie service. Element bingings zawiera specyfikacje odnośnie wykorzystywanego protokołu. <binding name="ProcessorSoapBinding" type="tns:Processor"> <soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/> <operation name="charge"> <soap:operation soapAction="" style="rpc"/> <input><soap:body use="literal" namespace="http://charge-it.com/Processor"/> </input> <output><soap:body use="literal" namespace="http://charge-it.com/Processor"/> </output> </operation> </binding> 10 Adres usługi Oprócz interfejsu końcowego, w oparciu o element service tworzony jest interfejs usługi: package com.charge_it; public interface ProcessorService extends javax.xml.rpc.Service{ public Processor getProcessorPort() throws javax.xml.rpc.ServiceException; public Processor getProcessorPort(URL portAddress) throws javax.xml.rpc.ServiceException; public String getProcessorPortAddress( ); } Interfejs usługi pozwala na utworzenie pośrednika (Processor), poprzez którego otrzymamy dostęp do usługi sieciowej. 11 Korzystanie z usługi sieciowej w obrębie EJB ... @Stateful public class AnyBean implements AnyRemote { private ProcessorService service; ... public void aMethod(...) throws EJBException{ ... try{ Processor processor = service.getProcessorPort(); processor.charge(customerName, card.number, expDate, card.type, price); ... }catch(Exception e) { throw new EJBException(e); } } } UWAGA: wystąpienie błędu (zwrócenie wyjątku) spowoduje anulowanie operacji wykonanych przez komponent w ramach transakcji, jednak nie spowoduje odwołania zmian wykonanych przez usługę sieciową. 12 Korzystanie z usługi sieciowej w obrębie EJB Parametry wstrzyknięcia określamy w deskryptorze wdrożenia ejbjar.xml: ... <ejb-name>AnyBean</ejb-name> <service-ref> <service-ref-name>service/ProcessorService</service-ref-name> <service-interface>com.charge_it.ProcessorService </service-interface> <wsdl-file>META-INF/wsdl/ChargeItProcessor.wsdl</wsdl-file> <jaxrpc-mapping-file>META-INF/mapping.xml</jaxrpc-mapping-file> <service-qname>chargeIt:ProcessorService</service-qname> <mapped-name>webservices/ProcessorService</mapped-name> <injection-target> <injection-target-class>AnyBean</injection-target-class> <injection-target-name>service</injection-target-name> </injection-target> </service-ref> ... 13 Korzystanie z usługi sieciowej w obrębie EJB Specyfikacja JAX-RPC wymaga dostarczenia odwzorowania, które wiąże WSDL i Javę. Zwykle jest ono umieszczone w pliku jaxrpcmapping.xml. Ponadto, w zależności od tego czy jesteśmy dostawcą czy odbiorcą usługi mogą specyfikacja wymaga dodatkowych plików opisujących usługę. Zwykle jednak producenci kontenerów EJB dostarczają narzędzi, które automatycznie generują wszystkie potrzebne pliki na podstawie interfejsu w Javie i kilku dodatkowych parametrów. 14 Korzystanie z usługi w programie klienckim ... public class Client { public static void main(String [] args) { ... try{ Context jndiContext = new javax.naming.InitialContext(); ProcessorService service = (ProcessorService)jndiContext. lookup("java:comp/env/service/ProcessorService"); Processor processor = service.getTravelAgentPort(); processor.charge(customerName, card.number, expDate, card.type, price); }catch (javax.naming.NamingException ne){ ne.printStackTrace(); }catch (java.rmi.RemoteException re){ re.printStackTrace(); }catch (javax.xml.rpc.ServiceException se){ se.printStackTrace(); } ... } ... } 15 Narzędzia Producenci kontenerów EJB dostarczają narzędzi pozwalających automatycznie generować potrzebne pliki. Przykład zadania Ant'a (JBoss): <target name="wstool"> <taskdef name="wstools" classname="org.jboss.ws.tools.ant.wstools"> <classpath refid="build.classpath" /> </taskdef> <wstools dest="dd/ws" config="wstools-config.xml"/> </target> oraz plik konfiguracyjny: <configuration xmlns="http://www.jboss.org/jbossws-tools"> <java-wsdl> <service name="ProcessorService" style="rpc" endpoint="com.charge_it.ProcessorService"/> <namespaces target-namespace="http://charge-it.com/Processor" type-namespace="http://charge-it.com/Processor"/> <mapping file="jaxrpc-mapping.xml"/> <webservices ejb-link="ProcessorBean"/> </java-wsdl> </configuration> 16 Narzędzia Przedstawiony kod generuje następujące pliki: ● ProcessorService.wsdl: definicja usługi w formacie WDSL. Niektóre dane mogą być uzupełnione po uruchomieniu usługi (np. adres SOAP). ● jaxrpc-mapping.xml: odwzorowanie pomiędzy typami WSDL i Javy. Może być wykorzystywany przez usługę i klienta. ● webservices.xml: deklaracja usługi. Ustaniawia połączenie pomiędzy adresem usługi a komponentem EJB, który ją realizuje. 17 JAX-RPC podsumowanie Usługa: ● interfejs punktu końcowego rozszerzający java.rmi.Remote, ● bezstanowy komponent sesyjny implementujący interfejs punktu końcowego, ● deskryptory: opis usługi w WSDL'u, jaxrpc-mapping.xml, webservices.xml. Klient: ● interfejs usługi i interfejs punktu końcowego, ● odwzorowanie danych: jaxrpc-mapping.xml. 18 JAX-WS Aby zdefiniować usługę sieciową w oparciu o bezstanowy komponent sesyjny zgodnie ze specyfikacją JAX-RPC należy wykonać sporo dodatkowej pracy związanej z przygotowaniem wymaganych plików opisujących usługę. Aby zredukować tą dodatkową pracę do minimum powstała alternatywna specyfikacja JAX-WS zgodna z EJB 3.0. 19 JAX-WS Aby zdefiniować usługę sieciową w oparciu o bezstanowy komponent sesyjny zgodnie ze specyfikacją JAX-RPC należy wykonać sporo dodatkowej pracy związanej z przygotowaniem wymaganych plików opisujących usługę. Aby zredukować tą dodatkową pracę do minimum powstała alternatywna specyfikacja JAX-WS zgodna z EJB 3.0: import javax.ejb.Stateless; import javax.jws.WebService; import javax.jws.WebMethod; @Stateless @WebService public class TravelAgentBean{ @WebMethod public String makeReservation(int cruiseId, int cabinId, int customerId, double price) { ... } } 20 Adnotacja @WebService package javax.jws; @Target({TYPE}) @Retention(value=RetentionPolicy.RUNTIME) public @interface WebService { String name( ) default ""; // nazwa usługi: wsdl:portType, // domyślnie nazwa klasy lub // interfejsu String targetNamespace( ) default ""; // domyślnie nazwa // pakietu String serviceName( ) default ""; // odwzorowane na // wsdl:service String wsdlLocation( ) default ""; // używane gdy istnieje już // plik WSDL String portName( ) default ""; } // odwzorowane na wsdl:port String endpointInterface( ) default ""; // deklaracja może być // oddzielona od // implementacji 21 Adnotacja @WebMethod Jeżeli bezstanowy komponent sesyjny oznacony jako @WebService nie posiada żadnej metody oznaczonej przez @WebMethod wtedy wszystrkie jego metody są udostępnione jako elementy usługi siecowej. W przeciwnym razie widoczne są tylko te oznaczone adnotacją. @Target({ElementType.METHOD}) @Retention(value = RetentionPolicy.RUNTIME) public @interface WebMethod{ String operationName( ) default ""; // wsdl:operation, // domyślnie nazwa metody String action( ) default ""; // nazwa akcji SOAP } 22 Adnotacja @WebParam Adnotacja @WebParam umożliwia dalszą kontrolę WSDL'a generowanego dla metody oznaczonej przez @WebMethod. @Target({PARAMETER})@Retention(value=RetentionPolicy.RUNTIME) public @interface WebParam { public enum Mode {IN, OUT, INOUT}; String name() default ""; // odwzorowywane na wsdl:part String targetNamespace() default ""; Mode mode() default Mode.IN; } boolean header() default false; // parametr w nagłówku SOAP // zamiast w treści (body) 23 Adnotacja @WebParam @WebMethod(operationName = "CheckStatus") public int checkStatus( @WebParam(name = "ReservationID") String reservationId, @WebParam(name = "CustomerID", mode = WebParam.Mode.OUT) javax.xml.ws.Holder<Integer> customerId){ ... } <message name="CheckStatus"> <part name="ReservationID" type="xsd:string"/> </message> <message name="CheckStatusResponse"> <part name="return" type="xsd:int"/> <part name="CustomerID" type="xsd:int"/> </message> <portType name="TravelAgent"> <operation name="CheckStatus" parameterOrder="ReservationID CustomerID"> <input message="tns:CheckStatus"/> <output message="tns:CheckStatusResponse"/> </operation> </portType> 24 Adnotacja @WebResult @WebMethod(operationName = "Reserve") @WebResult(name = "ReservationID") // oprócz name może zawierać // także targetNamespace() public String makeReservation(...) {...} <xs:element name="ReserveResponse" type="ReserveResponse"/> <xs:complexType name="ReserveResponse"> <xs:sequence> <xs:element name="ReservationID" type="xs:string" nillable="true"/> </xs:sequence> </xs:complexType> ... <message name="ReserveResponse"> <part name="parameters" element="tns:ReserveResponse"/> </message> ... <portType name="TravelAgent"> <operation name="Reserve"> <input message="tns:Reserve"/> <output message="tns:ReserveResponse"/> </operation> </portType> 25 Inne adnotacje @SoapBinding pozwala dookreślić postać wywołań SOPAP: @Target(value = {ElementType.TYPE}) @Retention(value = RetentionPolicy.RUNTIME) public @interface SOAPBinding { public enum Style {DOCUMENT, RPC}; // parametry mapowane na wsdl:part public enum Use {LITERAL,ENCODED}; public enum ParameterStyle {BARE, // jeden parametr w elemencie // root określający całą // wiadomość WRAPPED}; // wiele parametrów // w elemencie root Style style() default Style.DOCUMENT; Use use() default Use.LITERAL; //LITERTAL wymagane przez JAX-WS ParameterStyle parameterStyle() default ParameterStyle.WRAPPED; } @OneWay – w odpowiedzi przesyłana jest pusta wiadomość: brak elementu output w WSDL'u, 26 Rozdzielenie interfejsu i implementacji package com.titan.webservice; import javax.jws.WebService; @WebService public interface TravelAgentEndpoint{ public String makeReservation(int cruiseId, int cabinId, int customerId, double price); } package com.titan.webservice; import javax.jws.WebService; @WebService(endpointInterface = "com.titan.webservice.TravelAgentEndpoint") public class TravelAgentBean implements TravelAgentEndpoint { ... } 27 Punkt końcowy usługi Podobnie jak w JAX-RPC dostęp do usługi sieciowej realizowanej np. przez komponent EJB może być zrealizowany poprzez interfejs końcowy usługi i klasę usługi: package com.charge_it; import javax.jws.WebService; import javax.jws.soap.SOAPBinding; @WebService @SOAPBinding(style = SOAPBinding.Style.RPC) public interface Processor{ public int charge(String name, String number, java.util.Calendar expDate, String cardType, float amount); } 28 Klasa usługi package com.charge_it; import javax.xml.ws.WebServiceClient; import javax.xml.ws.WebEndpoint; @WebServiceClient(name="ProcessorService", targetNamespace="http://charge-it.com/Processor" wsdlLocation="http://charge-it.com/Processor?wsdl") public class ProcessorService extends javax.xml.ws.Service { public ProcessorService( ) { super(new URL("http://charge-it.com/Processor?wsdl"), new QName("http://charge-it.com/Processor","ProcessorService")); } public ProcessorService(String wsdlLoc, QName sName){ super(wsdlLoc, sName); } @WebEndpoint(name = "ProcessorPort") public Processor getProcessorPort( ) { return (Processor) super.getPort(new QName("http://chargeit.com/Processor","ProcessorPort"),Processor.class); } } 29 Dostęp do usługi @Stateful public class AnyBean implements AnyRemote { @WebServiceRef(ProcessorService.class) Processor processor; ... public void aMethod(...) throws EJBException { ... try { processor.charge(customerName, card.number, expDate, card.type, price); ... } catch(Exception e) { throw new EJBException(e); } } ... } 30 Podsumowanie Usługi sieciowe, ze względu na swoją elastyczność i uniwersalność zyskały bardzo mocną pozycję w środowisku aplikacji biznesowych. Większość rozwiązań integrujących różne systemy opiera się właśnie na usługach sieciowych. Specyfikacja JavaEE umożliwia tworzenie komponentów realizujących tekie usługi jak również korzystanie z nich. Jest to możliwe za pośrednictwem dwóch mechanizmów: JAXRPC i JAX-WS. 31