Kedra Architektury Systemów Komputerowych Wydział Elektroniki, Telekomunikacji i Informyki Politechniki Gdańskiej dr inż. Paweł Czarnul pczarnul@eti.pg.gda.pl Architektury usług internetowych INSTALACJA USŁUG SIECIOWYCH Z WYKORZYSTANIEM WSDD, DEFINIOWANIE PROCEDUR OBSŁUGI, PRZETWARZANIE WIADOMOŚCI W SYSTEMIE AXIS, SERIALIZACJA 1. WSTĘP i KONFIGURACJA LABORATORIUM 4 Temem niniejszego labororium jest konfiguracja zaawansowanych elementów serwera AXIS oraz instalacja i uruchamianie usług sieciowych z wykorzystaniem deskryptorów WSDD. Przed przystąpieniem do niniejszego labororium, należy zapoznać się z następującą literurą: 1. Instrukcje do labororium 2 oraz 3. 2. User s Guide systemu AXIS. http://ws.apache.org/axis/java/user guide.html 3. Architecture Guide systemu AXIS. http://ws.apache.org/axis/java/architectureguide.html 4. Reference Guide systemu AXIS. http://ws.apache.org/axis/java/reference.html 5. Przykłady dostarczone z systemem AXIS. Kalog samples/userguide/example*. Na potrzeby tego labororium zakładamy, że pracujemy jako użytkownik student z kalogiem domowym /home/student. Zakładamy, że treść pliku ~/.bashrc zawiera konfigurację dla AXIS 1.4 i Javy (należy zmienić kalog na dystrybucję Javy jeśli jest inna). export JAVA_HOME=/usr/local/jdk1.5.0_07 export AXIS_HOME=/home/student/axis 1_4 export AXIS_LIB=$AXIS_HOME/lib export AXISCLASSPATH=$AXIS_LIB/axis.jar:$AXIS_LIB/axis ant.jar: $AXIS_LIB/commons discovery 0.2.jar:$AXIS_LIB/commons logging 1.0.4.jar:\ $AXIS_LIB/jaxrpc.jar:$AXIS_LIB/saaj.jar:$AXIS_LIB/log4j 1.2.8.jar: $AXIS_LIB/wsdl4j 1.5.1.jar export CLASSPATH=$CLASSPATH:$AXISCLASSPATH
export PATH=$JAVA_HOME/bin:$PATH Zakładamy ponadto, że serwer Tomc został zainstalowany w kalogu /home/student/apache tomc 5.5.28 jak również podkalog webapps/axis dystrybucji AXIS został skopiowany do kalogu webapps serwera Tomc. Kompilacja i uruchomienie przykładów dostarczonych z Tomcem (prz poniżej na przykład dostarczony do tego labororium) wyglądają następująco (jeśli CLASSPATH nie zawiera $AXISCLASSPATH należy dodać opcję cp $AXISCLASSPATH:. Zarówno do kompilacji jak i uruchomienia): 1. Kompilacja klienta, usługi sieciowej oraz pliku procedur(y) obsługi: javac...*.java np.: javac samples/userguide/example4/*.java 2. Umieszczenie kodu na serwerze: cp../axis 1_1beta/samples/userguide/example4/*.class webapps/axis/web INF/classes/samples/userguide/example4/ 3. Uruchomienie serwera Tomc z zainstalowanym AXISem: startup.sh w kalogu bin kalogu głównego serwera Tomc. W kalogu, w którym uruchamiany jest serwer, znajdą się ewentualne pliki zapisywane przez procedurę obsługi. 4. Zainstalowanie procedury obsługi wraz z usługą: java org.apache.axis.client.adminclient samples/userguide/example4/deploy.wsdd 5. Uruchomienie klienta: java samples/userguide/example4/client 2. PRZETWARZANIE WIADOMOŚCI, PROCEDURY OBSŁUGI AXIS jest narzędziem, które służy przetwarzaniu wiadomości. Ścieżka przetwarzania wiadomości na serwerze wygląda następująco: 1. TransportListener nasłuchuje na przyjście wiadomości, następnie tworzy obiekt MessageContext, który jest przekazywany do przetwarzania serwerowi AXIS. Nasłuchiwanie może odbywać się na porcie 80, ale może również polegać na sprawdzeniu pojawienia się pliku o określonej nazwie w kalogu i przekzaniu jego zawartości do przetwarzania przez serwer. 2. W ramach AXIS Engine, wiadomość może być przetwarzana przez łańcuchy procedur obsługi, zarówno przed wywołaniem właściwej metody usługi sieciowej jak i po. Etapy, na których procedury obsługi (handlers) mogą być wywołane są następujące: a. Warstwa transport dla specyficznego protokołu b. Global dla wszystkich wiadomości c. Service dla konkretnej usługi sieciowej. d. Wywołanie właściwej metody usługi sieciowej e. Service
f. Globar g. Warstwa transport Odpowiednia konfiguracja usługi sieciowej wraz z procedurami obsługi, które mogą być zainstalowane na poszczególnych etapach, opisana jest w: 1. dokumentacji AXIS. http://ws.apache.org/axis/java/reference.html 2. przykładzie w instrukcji użytkownika AXIS. http://ws.apache.org/axis/java/userguide.html#customdeploymentintroducingwsdd Instalacja usługi sieciowej z dodkowymi procedurami obsługi, wywoływanymi przed lub po wywołaniu właściwej metody usługi sieciowej, polega na zdefiniowaniu odpowiedniego deskryptora WSDD (Web Service Deployment Descriptor), który pozwalana zdefiniowanie: 1. usługi sieciowej element service wraz z: a. nazwą, b. klasą zawierającą implementację: <parameter name="classname" value=" "/> c. listą metod, które można wywołać w usłudze sieciowej <parameter name="allowedmethods" value="*"/> wszystkie metody d. procedurami obsługi (handlers) lub łańcuchami (ciągi procedur obsługi), które mogą być wywołane dla danej usługi sieciowej przed lub po wywołaniu właściwej metody usługi sieciowej: i. element requestflow przed wywołaniem usługi ii. element responseflow po wywołaniu usługi Elementy te, zdefiniowane wewnątrz elementu service tyczą się danej usługi sieciowej, mogą być również zdefiniowane globalnie (element GlobalConfigurion, http://ws.apache.org/axis/java/reference.html), dotyczą wówczas wszystkich wywołań możliwe jest również użycie elementów requestflow oraz responseflow dla warstwy transport element transport (http://ws.apache.org/axis/java/reference.html). Elementy requestflow oraz responseflow mogą zawierać łańcuchy oraz procedury obsługi, wywołane zostaną zgodnie z kolejnością, w której zostały wyspecyfikowane. Procedury obsługi lub łańcuchy przywoływane są po nazwie, zdefiniowane mogą być poza elementem service. Procedury obsługi mogą czytać argumenty w postaci pary nazwa, wartość, wyspecyfikowane w deskryptorze WSDD. Przykład:
<handler name="acceptreject" type="java:deadlinehandler"> <parameter name="deadline" value="7"/> </handler> W powyższym przykładzie, który może być fragmentem usługi sieciowej, ww. procedura obsługi może być pierwszym etapem na drodze do sprawdzenia czasu wywołania usługi. Procedura obsługi może sprawdzać czy wywołanie następuje przed czy po wyznaczonym terminie. Termin ten zdefiniowany jest w parametrze o nazwie deadline, który kod procedury obsługi może odczytać. Fragment kodu odpowiedniej procedury obsługi mógłby wyglądać następująco: public class DeadlineHandler extends BasicHandler { public void invoke(messagecontext msgcontext) throws AxisFault { try { Calendar cal = new GregorianCalendar(); // Get the components of the time int hour = cal.get(calendar.hour_of_day); String deadline = (String)getOption("deadline"); if (Integer.valueOf(deadline).intValue()<hour) throw new AxisFault("Deadline passed", "Deadline passed!", null, null); cch (Exception e) { throw AxisFault.makeFault(e); Dane o żądaniu, w szczególności informacje czy procedura obsługi znajduje się za właściwą metodą usługi sieciowej czy po, dostęp do właściwych danych żądania itp. można uzyskać poprzez obiekt MessageContext (http://ws.apache.org/axis/java/apidocs/org/apache/axis/messagecontext.html). Przykładowo, poniższy kod sprawdza czy procedura znajduje się przed czy po wywołaniu właściwej metody usługi sieciowej: if (msgcontext.getpastpivot()) writer.println("yes");
Wiele informacji uzyskać można poprzez wywołanie metody getproperty obiektu MessageContext. Możliwe do wykorzystania nazwy rybutów znaleźć można np. w http://ws.apache.org/axis/java/apidocs/org/apache/axis/transport/http/httpconstant s.html. Należy pamiętać o dołączeniu właściwej linijki import do kodu procedury obsługi. Referencję do obiektu MessageContext można pobrać z argumentu metody invoke procedury obsługi bądź też wywołując styczną metodę MessageContext.getCurrentContext(). W ten sposób, z wykorzystaniem procedur obsługi, można zaimplementować przetwarzanie zgłoszenia publikacji: 1. Procedura obsługi sprawdzanie terminu zgłoszenia 2. Procedura obsługi sprawdzanie długości zgłoszenia itp. 3. Właściwa metoda usługi sieciowej zapis publikacji do bazy danych 4. procedura obsługi wysłanie emaila do zgłaszającego publikację, iż została przyjęta do recenzji. Implementacja taka pozwala na uniknięcie błędów poprzez implementację mniejszych modułów, polepsza konfigurowalność aplikacji poprzez definiowanie parametrów dla procedur obsługi (serwis może być wykorzystany na różnych konferencjach z różnymi parametrami jak da zgłoszenia, maksymalny rozmiar itp.) oraz możliwość wykorzystania pojedynczych procedur w różnych usługach sieciowych (np. procedura sprawdzająca termin zgłoszenia). Procedura obsługi może również wygenerować wyjątek. Dla przykładu, dzielenie przez 0, a więc kod procedury obsługi: int a=2/0; writer.close(); cch (Exception e) { throw AxisFault.makeFault(e); pokaże po stronie klienta: java samples/userguide/example4/client Mapping Exception to AxisFault AxisFault faultcode: {http://xml.apache.org/axis/server.userexception faultstring: java.lang.arithmeticexception: / by zero
faultactor: null faultdetail: stacktrace: java.lang.arithmeticexception: / by zero samples.userguide.example4.loghandler.invoke(loghandler.java:105) Artykuł dostępny pod adresem http://www 128.ibm.com/developerworks/xml/library/x tipsoap.html pokazuje w jaki sposób można użyć nagłówków SOAP w celu implementacji priorytetów wywołań usług sieciowych i ewentualnego opóźnienia usług o niższym priorytecie. Klient, przy wywołaniu usługi sieciowej określa jej priorytet, jest on następnie odczytywany przez procedurę obsługi, która wprowadza dodkowe opóźnienie w zależności od zadanego priorytetu. 3. KOMPLETNY PRZYKŁAD W poniższym przykładzie zakładamy usługę analogiczną do tej testowanej w labororiach 2 i 3, a więc zdalnego wywołania aplikacji (proszę zwrócić uwagę na nieco inny URL usługi w kliencie). Dodkowo implementujemy procedurę obsługu, która pobiera parametr w postaci godziny, po której wywołanie nie będzie uruchamiane. Procedura wywołana będzie przed wywołaniem faktycznej usługi. Aplikacje kompilujemy za pomocą javac *.java a następnie kod (pliki *.class) usługi jak i procedury obsługi powinny znaleźć się w kalogu: /home/student/apache tomc 5.5.28/webapps/axis/WEB INF/classes/ Następnie po uruchomieniu serwera (startup.sh w kalogu serwera Tomc) wdrażamy usługę na serwerze. Powinna być wówczas widoczna w głównej konsoli AXIS widocznej przez przeglądarkę internetową. Wdrożenie następuje przez wywołanie: java org.apache.axis.client.adminclient deploy.wsdd W dalszej kolejności, po skompilowaniu, można wywołać program klienta: java RunTaskClient emacs W przypadku gdy termin został przekroczony, otrzymujemy następujący kompunik: Exception in thread "main" AxisFault faultcode: {http://xml.apache.org/axis/deadline passed faultsubcode: faultstring: Deadline passed! faultactor: faultnode: faultdetail: {http://xml.apache.org/axis/hostname:wolf
Deadline passed! org.apache.axis.message.soapfaultbuilder.creefault(soapfaultbuilder.java:222 ) org.apache.axis.message.soapfaultbuilder.endelement(soapfaultbuilder.java:12 9) org.apache.axis.encoding.deserializioncontext.endelement(deserializioncontext.java:1087) com.sun.org.apache.xerces.internal.parsers.abstractsaxparser.endelement(abstra ctsaxparser.java:633) com.sun.org.apache.xerces.internal.impl.xmlnsdocumentscannerimpl.scanendele ment(xmlnsdocumentscannerimpl.java:719) com.sun.org.apache.xerces.internal.impl.xmldocumentfragmentscannerimpl$frag mentcontentdispcher.dispch(xmldocumentfragmentscannerimpl.java:1685) com.sun.org.apache.xerces.internal.impl.xmldocumentfragmentscannerimpl.scan Document(XMLDocumentFragmentScannerImpl.java:368) com.sun.org.apache.xerces.internal.parsers.xml11configurion.parse(xml11confi gurion.java:834) com.sun.org.apache.xerces.internal.parsers.xml11configurion.parse(xml11confi gurion.java:764) com.sun.org.apache.xerces.internal.parsers.xmlparser.parse(xmlparser.java:148) com.sun.org.apache.xerces.internal.parsers.abstractsaxparser.parse(abstractsax Parser.java:1242) javax.xml.parsers.saxparser.parse(saxparser.java:375) org.apache.axis.encoding.deserializioncontext.parse(deserializioncontext.java: 227) org.apache.axis.soappart.getassoapenvelope(soappart.java:696) org.apache.axis.message.getsoapenvelope(message.java:435) org.apache.axis.handlers.soap.mustunderstandchecker.invoke(mustunderstandche cker.java:62) org.apache.axis.client.axisclient.invoke(axisclient.java:206) org.apache.axis.client.call.invokeengine(call.java:2784) org.apache.axis.client.call.invoke(call.java:2767)
org.apache.axis.client.call.invoke(call.java:2443) org.apache.axis.client.call.invoke(call.java:2366) org.apache.axis.client.call.invoke(call.java:1812) RunTaskClient.main(RunTaskClient.java:47) Kod usługi: import java.io.*; public class RunTaskServerDeployed { public int RunTask(String taskname) { try { Process p = Runtime.getRuntime().exec(taskname); cch (IOException e1) { System.err.println(e1); System.exit(1); return 0; Kod procedury: import org.apache.axis.axisfault; import org.apache.axis.handler; import org.apache.axis.messagecontext; import org.apache.axis.handlers.basichandler; import java.io.fileoutputstream; import java.io.printwriter; import java.util.de; import java.util.gregoriancalendar; import java.util.calendar; public class DeadlineHandler extends BasicHandler { public void invoke(messagecontext msgcontext) throws AxisFault { try { Calendar cal = new GregorianCalendar(); // Get the components of the time int hour = cal.get(calendar.hour_of_day);
String deadline = (String)getOption("deadline"); if (Integer.valueOf(deadline).intValue()<hour) throw new AxisFault("Deadline passed", "Deadline passed!", null, null); cch (Exception e) { throw AxisFault.makeFault(e); kod klienta: // Autor: Paweł Czarnul, KASK, WETI, Politechnika Gdańska // pczarnul@eti.pg.gda.pl import org.apache.axis.client.call; import org.apache.axis.client.service; import org.apache.axis.encoding.xmltype; import org.apache.axis.utils.options; import javax.xml.rpc.parametermode; public class RunTaskClient { public stic void main(string [] args) throws Exception { Options options = new Options(args); String endpoint = "http://localhost:" + options.getport() + "/axis/services/runtaskserverdeployed"; args = options.getremainingargs(); String method = "RunTask";
String s1 = new String(args[0]); Service service = new Service(); Call call = (Call) service.creecall(); call.settargetendpointaddress( new java.net.url(endpoint) ); call.setoperionname( method ); call.addparameter( "op1", XMLType.XSD_STRING, ParameterMode.IN ); call.setreturntype( XMLType.XSD_INT ); Integer ret=(integer) call.invoke( new Object [] { s1 ); System.out.println("Got result : " + ret); Kod pliku wdrożeniowego deploy.wsdd: <deployment xmlns="http://xml.apache.org/axis/wsdd/" xmlns:java="http://xml.apache.org/axis/wsdd/providers/java"> <! define the logging handler configurion > <handler name="acceptreject" type="java:deadlinehandler"> <parameter name="deadline" value="7"/> </handler> <! define the service, using the log handler we just defined > <service name="runtaskserverdeployed" provider="java:rpc"> <requestflow> <handler type="acceptreject"/> </requestflow> <parameter name="classname" value="runtaskserverdeployed"/> <parameter name="allowedmethods" value="*"/> </service>
</deployment> 4.STYLE PRZETWARZANIA WIADOMOŚCI W AXIS AXIS pozwala na przetwarzanie wiadomości w różnych stylach, z których najpopularniejszym jest RPC, polegający na wykorzystaniu usług sieciowych, protokołów SOAP np. z http jako implementacji zdalnego wołania procedur. Z tego punktu widzenia, usługi sieciowe są w dużej mierze równoważne technologiom CORBA itp., z wyjątkiem użycia innych protokołów i szczegółów implementacyjnych. Z drugiej strony, oprócz stylów dokument i wrapped (http://ws.apache.org/axis/java/userguide.html#servicestylesrpcdocumentwrappedandmessage), AXIS umożliwia przetwarzanie wiadomości na poziomie protokołu SOAP, metoda usługi sieciowej może mieć więc dostęp do poszczególnych elementów wiadomości w formacie XML. Metody obsługujące po stronie serwera mogą mieć następujące sygnury (http://ws.apache.org/axis/java/userguide.html#servicestylesrpcdocumentwrappedandmessage): public Element [] method(element [] bodies); public SOAPBodyElement [] method (SOAPBodyElement [] bodies); public Document method(document body); public void method(soapenvelope req, SOAPEnvelope resp); AXIS można więc wykorzystać jako serwer przetwarzający dokumenty XML. 5. SERIALIZACJA AXIS umożliwia automyczną serializację i deserializację, a więc i przezroczyste dla programisty przesyłanie typów prostych, ale również klas, które zgodnie z wymaganiami dla JavaBeans zawierają metody set i get. Dla takich klas, wystarczy w deskryptorze WSDD wyspecyfikować nazwę klasy: <beanmapping qname="ns:local" xmlns:ns="somenamespace" languagespecifictype="java:nazwaklasy"/> Dla klas, które nie są zgodne z wymaganiami dla JavaBeans, należy dostarczyć kod serializujący i deserializujący kod danej klasy oraz zapisać powiązanie w elemencie deskryptora WSDD w sposób następujący: <typemapping qname="ns:local" xmlns:ns="somenamespace"
languagespecifictype="java:nazwaklasy" serializer="serializor" deserializer="deserializor" encodingstyle="http://schemas.xmlsoap.org/soap/encoding/"/>