2008 06 Java Microedition – metody integracji aplikacji [Inzynieria Oprogramowania]
Inżynieria oprogramowania Java Microedition metody integracji aplikacji Albert Wachowicz plikacje Java Microedition (konfiguracji Jednym z lepszych zestawień danych technicz- CLDC) działające na urządzeniach przeno- nych urządzeń mobilnych różnych producentów Aśnych mają zazwyczaj ograniczone zaso- jest strona J2MEPolish. by sprzętowe. W większości przypadków limitowa- Kolejnym ważnym aspektem jest profil urzą- na pamięć oraz wolne procesory na urządzeniach dzenia Mobile Information Device Profile w skró- powodują, że pewne zadania są mało wydajne lub niemożliwe do zrealizowania. Rozwiązaniem tego Listing 1. Przykładowa metoda wysyłająca problemu może być przeniesienie części funkcjo- wiadomość poprzez gniazdo nalności aplikacji na stronę serwera. W ten sposób realizuje się ubogiego klienta, który wymaga mniej void sendSocketMsg(String destAddr, String msg) { zasobów przy zakładanej funkcjonalności. Nato- try { miast serwer przejmuje wymagające większych //utworzenie gniazda zasobów zadania i zwraca do aplikacji klienckiej SocketConnection client = tylko rezultaty swojej pracy. (SocketConnection) Niniejszy artykuł skupi się na sposobach in- Connector.open("socket://" tegracji aplikacji klienta JME z aplikacją serwera. + destAddr + ":3000"); Zostanie zaprezentowany krótki przegląd metod za pomocą których można skomunikować klien- //otwarcie strumienii wejscia/wyjscia ta z serwerem. InputStream is = client.openInputStr Gruntownie zostaną opisane wybrane mecha- eam(); nizmy integracji, które obecnie są najczęściej sto- OutputStream os = client.openOutputSt sowane. Na koniec zostanie przedstawiona ocena ream(); wybranych metod w odniesieniu do wszechstron- nego zastosowania. // wysłanie wiadomosci doserwera os.write(msg.getBytes()); Uwagi na temat technologii JME os.write('\n'); W technologii JME paczki z bibliotekami dostępne są bezpośrednio w maszynie wirtualnej KVM (Kilo- // odczytanie odpowiedzi (dla byte Virtual Machine) urządzenia dzięki czemu pro- przykladu do znaku konca gramista nie musi martwić się o ich dostępność. Z linii \n lub gdy serwer drugiej strony istnieją rozwiązania, które wymagają zamknie polaczenie) dołączenia odpowiedniej biblioteki zawartej w ar- int c = 0; chiwum jar. Wiąże się to niestety ze wzrostem roz- StringBuffer sb = new StringBuffer(); miaru (archiwum jar) aplikacji co ma istotne zna- while ((c = is.read()) != -1 && (c!='\ czenia w przypadku limitów maksymalnej wielko- n')) { ści narzuconej przez producentów. sb.append((char)c); Zagadnienie to nie stanowi jednakże tematu } niniejszego artykułu. Zainteresowanym polecam strony producentów urządzeń, gdzie w większości System.out.println("[Serwer]"+ przypadków tego rodzaju informacje są dostępne. sb.toString()); //analiza odpowiedzi... Albert Wachowicz pracuje na stanowisku Software // zamkniecie strumieni oraz Specialist w BLStream wchodzącym w skład Grupy polaczenia BLStream. Grupa BLStream powstała by efektywniej is.close(); wykorzystywać potencjał dwóch, szybko rozwijają- os.close(); cych się producentów oprogramowania BLStream client.close(); i Gamelion. Firmy wchodzące w skład grupy specja- lizują się w wytwarzaniu oprogramowania dla klien- } catch (IOException ex) { tów korporacyjnych, w rozwiązaniach mobilnych oraz ex.printStackTrace(); produkcji i testowaniu gier. } Kontakt z autorem: albert.wachowicz@gmail.com } 58 www.sdjournal.org Software Developer s Journal 6/2008 Java Microedition cie MIDP oraz konfiguracja Connected Limited Device Con- " Komunikacja niskopoziomowa UDP, TCP, TLS(SSL); figuration CLDC. Określa się w nich zbiór interfejsów i klas " HTTP/HTTPS; dostępnych w danej KVM. Aktualnie dominującym profilem " RMI J2MEPolishRMI, implementacja RMI bazująca na jest MIDP 2.0 w połączeniu z konfiguracją CLDC 1.1 (lub gniazdach; 1.0). Jednakże istnieją także urządzenia określane mianem " XML-RPC kXMLRPC, J2MEPolishRPC; wersji MIDP 1.0. W artykule zostanie zaznaczone od któ- " Web Services kSOAP[5], klient WebServices w NetBe- rej wersji profilu dostępny jest dany mechanizm jeśli bę- ans, JSR-172; dzie to istotne. " WMA SMS/MMS; " SIP; Spis metod integracyjnych " SyncML kSync, implementacja SyncML; Biorąc pod uwagę ogólne standardy komunikacji siecio- " Peer2Peer JXTA; wej w JME można wyróżnić między innymi następujące " Bluetooth; mechanizmy: " Inne: Listing 2. Metoda nasłuchujące na TCP Listing 3. Przykładowa metoda POST public void wait4TcpConnection() { private void doSendHTTP(String request, String url){ try { String responseStr = null; // utworzenie serwera nasluchujacego na porcie try 3000 { ServerSocketConnection server = //utworzenie polaczenia HTTP i ustawienie (ServerSocketConnection) nagłowka Connector.open("socket://:3000"); HttpConnection conn = (HttpConnection)Connector // oczekiwanie na polaczenia .open(url); System.out.println("Oczekiwanie na conn.setRequestMethod(HttpConnection.POST); polaczenie..."); conn.setRequestProperty("Content-Length", SocketConnection client = (SocketConnection) Integer.toString(reques server.acceptAndOpen(); t.length())); System.out.println("Zaakceptowanie polaczenia z //wyslanie wiadomosci adresu" + client.getAddress()); OutputStream out = conn.openOutputStream(); // utworzenie strumienie wejscia/wyjscia int requestLength = request.length(); InputStream is = client.openInputStream(); for (int i = 0; i < requestLength; ++i){ OutputStream os = client.openOutputStream(); out.write(request.charAt(i)); // czytanie danych ze strumienia (dla przykladu } do znaku konca linii \n lub gdy InputStream in = conn.openInputStream(); klient zamknal polaczenie) int c; StringBuffer responseBuf; StringBuffer sb = new StringBuffer(); long length = conn.getLength(); while (((c = is.read()) != -1) && (c!='\n')) { if (length > 0){ sb.append((char) c); responseBuf = new StringBuffer((int)length); } } else{ System.out.println("[KLIENT]" + sb.toString()); responseBuf = new StringBuffer(); //analiza wiadomosci ... } //odczytanie odpowiedzi //wyslanie odpowiedzi int ch; String responseMsg = new String("Odpowiedz while ((ch = in.read()) != -1) serwera OK\n"); { os.write(responseMsg.getBytes()); responseBuf.append((char)ch); } // zamkniecie strumienii oraz polaczenia responseStr = responseBuf.toString(); is.close(); System.out.println(responseStr.toString()); os.close(); }catch (IOException e){ client.close(); e.printStackTrace(); server.close(); }catch (SecurityException e){ } catch (IOException ex) { e.printStackTrace(); ex.printStackTrace(); } } } } Software Developer s Journal 6/2008 www.sdjournal.org 59 Inżynieria oprogramowania " JSON, konkretny obiekt typu Connection. Na Rysunku 1 przedsta- " Burlap, wiona jest hierarchia interfejsów, które mogą implemen- " JINI, tować obiekty zwrócone przez metodę Connector.open(). " GASP, Przezroczysty sposób tworzenia połączenia daje uniwer- " OpenDMTP, salną metodę, która zgłasza wyjątek ConnectionNotFoun- " Payment API, dException w przypadku gdy urządzenie nie implementuje " Jsch. określonego połączenia. Komunikacja TCP niskopoziomowa oraz HTTP Transmission Control Protocol jest połączeniowym proto- Gniazda są mechanizmem komunikacji definiującym inter- kołem zapewniającym niezawodną komunikację strumie- fejs programowania do wymiany informacji. W Java Micro- niową. Ustanawia dwukierunkową współpracę między ho- edition (MIDP 2.0) można zrealizować połączenia gniazd stami z możliwością sterowania przepływem (Rysunek 2). wykorzystując UDP, TCP i TLS. Kontroluje poprawność oraz kolejność pakietów danych i Ogólny szkielet nawiązania połączenia w JME wykony- oczekuje potwierdzania ich odbioru. wany jest przez następującą metodę fabryki Connector.ope- W przypadku wystąpienia błędów komunikacyjnych po- n("protocol:address;parameters") z paczki javax.microedi- trafi przeprowadzić retransmisję. Protokół TCP nie zobo- tion.io . Metoda zwraca określony przez parametr protocol wiązuje się jednak na zrealizowanie połączenia w określo- nym czasie. Przy nadmiernym obciążeniu łącz daje się zaobserwo- Listing 4. HttpServlet storna serwera http wać nagłe spowolnienie transmisji. Spowodowane to jest public class DeliveryServlet extends HttpServlet między innymi błędnymi pakietami i wysyłaniem żądań ich { ponownej retransmisji. public void init(ServletConfig config) throws TCP w Java Microedition realizowane jest poprzez wy- ServletException{ wołanie metody Connector.open oraz określenie minimalnie super.init(config); parametrów: protocol jako socket oraz address jako adre- ... su hosta docelowego wraz z portem. Przykładową metodę } wysyłającą wiadomość typu String na określony adres po- public void doPost(HttpServletRequest request, dany w parametrach wywołania metody jest zaprezentowa- HttpServletResponse response) ny na Listingu 1. throws ServletException, IOException Natomiast aplikacja nasłuchująca i wykorzystująca { gniazda TCP jest przedstawiona na Listingu 2. Została ona //odebranie wiadomosci od klienta i analiza jej zrealizowana w JME w celu zobrazowania możliwości tej BufferedReader br = request.getReader(); technologii. Równie dobrze serwer oczekujący na połącze- String buf = br.readLine(); nia może być zaimplementowany na dowolnej maszynie ob- System.out.println(buf); sługującej gniazda TCP. //operacje zwiazane z otrzymanymi danymi ... //przygotowanie odpowiedzi do klienta i wysłanie jej TLS(SSL) response.setContentType("text/html"); Transport Layer Security jest protokołem bezpieczeństwa, PrintWriter out = response.getWriter(); który bazuje na starszym protokole Secure Socket Layer out.println("Odpowiedz serwera OK"); (SSL). out.close(); Ze względu na brak w TCP mechanizmów ochrony prze- } syłanych danych wykorzystuje się TLS. Umiejscowiony jest ... on pomiędzy warstwą TCP a warstwą aplikacji co zapew- nia prostotę jego wykorzystania w programie. Umożliwia szyfrowanie danych, potwierdzanie tożsamości serwera/ Listing 5. Interfejs Serwera RMI klienta, zapewnianie integralności przesyłanych komuni- katów. W JME (MIDP 2.0) TLS realizuje się po niewielkich package pl.awa; modyfikacjach kodu (Rysunek 3). public interface RMIServer extends Remote { //metoda dodaje uzytkownika Bibliografia public User addUser( String name, String password, Address address) " XML-RPC zdalne wywoływanie procedur oparte na języku throws RemoteException; XML http://www.xmlrpc.com . " WMA Wireless Messaging API zbiór klas i metod do ob- //zwraca obiekt klasy Address sługi komunikacji SMS/MMS public Address getAddress( String userName ) " http://java.sun.com/products/wma/index.jsp throws RemoteException; " SIP Session Initiation Protocol protokół inicjowania sesji " http://developers.sun.com/mobility/apis/articles/sip/ 60 www.sdjournal.org Software Developer s Journal 6/2008 Java Microedition HTTP leceń GET lub POST. Synchronicznie tworzone są odpo- Hypertext Transfer Protocol może służyć jako nośnik infor- wiedzi na żądanie (response). macji między MIDletem a serwerem. Określa on formę żą- Ze względu na to że jest to protokół bezstanowy, nie dań klienta, które tworzymy między innymi za pomocą po- zachowuje informacji o poprzednich transakcjach stosu- je się mechanizm cookies. W przypadku Java Microedition można wykorzystać zapis do pliku w RMS lub systemie pli- Listing 6. Klasy implementujące interfejs Serializacji ków (FileConnection API) urządzenia aby zapamiętać stan import de.enough.polish.io.Serializable; transakcji. Standardowo w konfiguracji CLDC wykorzystu- public class User implements Serializable { je się metodę Connector.open z określeniem protokołu http: public String name; // oraz podaniem adresu serwera. Obiekt typu HttpConnec- public Address address; tion utworzony w ten sposób posiada metodę setRequest- public String password; Method określającą rodzaj żądania HttpConnection.POST lub } HttpConnection.GET. W przypadku wywołania POST można zdefiniować nagłówek http poprzez metodę setRequestPro- import de.enough.polish.io.Externalizable; perty( name , value ) jak to jest pokazane na przykłado- public class Address implements Externalizable { wym Listingu 3. private String street; Właściwa treść wiadomości do serwera jest przesyła- private String city; na strumieniowo analogicznie jak w gniazdach. Kod odpo- public Address() { wiedzi serwera otrzymujemy za pomocą metod getRespon- // wymagany konstruktor seCode, która zwraca statyczną wartość typu integer okre- } ślającą kod (zdefiniowaną w klasie HttpConnection) lub ge- public Address(String street, String city){ tResponseMessage w przypadku gdy istotna jest treść od- super(); powiedzi. this.street = new String(street); Natomiast stronę serwera można zrealizować w dowol- this.city = new String(city); nej technologii obsługującej HTTP. W artykule zostanie po- } kazana implementacja za pomocą Java Servlet. Na Listn- public void read(DataInputStream in) throws IOException { this.street = in.readUTF(); Listing 10. Implementacja serwera RMI this.city = in.readUTF(); } import de.enough.polish.rmi.RemoteException; public void write(DataOutputStream out) throws import de.enough.polish.rmi.RemoteHttpServlet; IOException { public class GameServerImpl out.writeUTF( this.street ); extends RemoteHttpServlet out.writeUTF( this.city ); implements GameServer } { } public User addUser( String name, String password, Address address) throws RemoteException{ Listing 7. Poprawne wywołanie metody RemoteClient.open //... //utworzenie obiektu klasy User ktory bedzie zwrocony this.server = (RMIServer) RemoteClient.open( przez metode "pl.awa.RMIServer", User user = new User(); "http://localhost:8080/awa/myservice" ); user.name = name; user.password = password; user.address = address; Listing 8. Błędne wywołanie metody RemoteClient.open //... return user; String myInterfaceName = "pl.awa.RMIServer"; } this.server = (RMIServer) RemoteClient.open( public Address getAddress( String userName ) throws myInterfaceName, RemoteException{ "http://localhost:8080/awa/myservice" ); //... //wyszukiwanie uzytkownika po nazwie z bazy danych String street = getStreetFromDB(userName); Listing 9. Zdalne wywołanie metody serwera String city = getCityFromDB(userName); Address address = new Address(street, city); //... //... User user = this.server.addUser( name, password, address); return address; System.out.println("Name:" + user.name.toString()); } //... } Software Developer s Journal 6/2008 www.sdjournal.org 61 Inżynieria oprogramowania Connection StreamConnectionNotifier DatagramConnection InputConnection OutputConnection StreamConnection ServerSockedConnection UDPDatagramConnection ContentConnection (MIDP 1.0) SockedConnection HttpConnection (MIDP 1.0) SecureConnection Rysunek 1. Hierarchia interfejsów komunikacji w JME gu 4 wykorzystany zostaje interfejs HttpServlet oraz im- Cała idea opiera się na traktowaniu obiektów zdalnych plementacja dwóch jego metod doGET oraz doPOST, które re- w ten sam sposób jak tych działających lokalnie. W konfi- alizują określone w nazwie żądania i generują odpowiedzi guracji CLDC JME standardowo nie można spotkać biblio- do klienta. Aplikację serwerową można uruchomić na kon- teki odpowiedzialnej za obsługę RMI. Istnieje gotowe roz- tenerze wspierającym serwety jak np. Apache Tomcat lub wiązanie opcjonalne J2MEPolish RMI, które jest dostęp- JBoss. Odnośnie wykorzystania HTTPS dostępnego od ne w dwóch licencjach GPL oraz w komercyjnej Commer- MIDP 2.0 to analogia użycia jest podobna jak w przypad- cial License. ku TLS. Za pomocą tego narzędzia można w przyjemy i szybki sposób zaimplementować mobilnego klienta komunikujące- RMI go się ze zdalnym serwerem. Wszystkie wywołania metod Remote Method Invocation jest mechanizmem, który organizuje komunikację między obiektami Java działającymi na różnych maszynach wirtualnych w środowisku rozproszonym. Techni- TCP ka ta umożliwia zdalne wywołania metod (RPC) obiektów w ję- zyku Java w przezroczysty sposób dla programisty ukrywając SocketConnection socketConnection = niskopoziomowe szczegóły związane z obsługą protokołów. (SocketConnection) Connection.open("socket://hostName:5000"); socketConnection.setSocketOption(SocketConnection.DELAY, 0); OutputSteream os = socketConnection.openOutputStream()); Klient Serwer Strumień danych lub komunikaty aplikacji MIDIet Serwer TLS (SSL) Pakiety SecureConnection socketConnection = TCP TCP (SecureConnection) Connector.open("ssl://hostName:5001"); String secureProtocolName = secureConnection.getSecurityInfo().getProtocolName(); Pakiety IP IP IP OutputSteream os = secureConnection.openOutputSteream(); Rysunek 2. Komunikacja poprzez gniazda TCP Rysunek 3. Transformacja na gniazda bezpieczeństwa TLS 62 www.sdjournal.org Software Developer s Journal 6/2008 Framework Interfaces CLDC Generic Connection MIDP 2.0 Interfaces Java Microedition oraz dostępność obiektów dają wrażenie ich lokalnego wy- W kolejnym kroku należy zdefiniować zbiór klas, któ- stępowania po stronie klienta i vice versa. Aby to uzmysło- re będą wspólne dla obydwu stron. Klasy te muszą imple- wić drogiemu Czytelnikowi w dalszej części zostaną przed- mentować interfejsy serializacji de.enough.polish.io.Seria- stawione kolejne kroki jakie należy podjąć aby to zrealizo- lizable lub de.enough.polish.io.Externalizable. Serializacja wać. w najprostszym określeniu polega na procesie przekształ- Na pierwszym etapie definiujemy zdalny interfejs, który bę- cenia obiektów do postaci strumienia bajtów. Dzięki zasto- dzie wspólnym protokołem między klientem a serwerem. Okre- sowaniu tego mechanizmu w RMI można przesyłać obiek- śla się w nim metody, które będzie udostępniał serwer. Jak ty poprzez strumieniowy protokół sieciowy. W klasie imple- można zauważyć na Listingu 5 wymagane jest aby dziedziczył mentującej Serializable programista nie musi martwić się on po interfejsie de.enough.polish.rmi.Remote oraz każda me- o proces serializacji dokonuje się on automatycznie jak to toda musi mieć możliwość zgłaszania wyjątku de.enough.po- widać na Listingu 6. Natomiast różnica w klasie implemen- lish.rmi.RemoteException. tującej Externalizable wynika z tego że należy zaimple- Listing 11. Przykładowy plik WSDL usługi Wheather soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://calculator.me.org/" xmlns:xsd="http: //www.w3.org/2001/XMLSchema" xmlns="http://schemas.xmlsoap.org/wsdl/" targetNamespace="http:// calculator.me.org/" name="CalculatorWSService">
Software Developer s Journal 6/2008 www.sdjournal.org 63 Inżynieria oprogramowania Listing 12. Klasa pnia wygenerowany automatycznie public class CalculatorWSService_Stub implements } CalculatorWSService, } javax.xml.rpc.Stub { public int add(int i, int j) throws private String[] _propertyNames; java.rmi.RemoteException { private Object[] _propertyValues; Object inputObject[] = new Object[] { new Integer(i), public CalculatorWSService_Stub() { new Integer(j) _propertyNames = new String[] { ENDPOINT_ADDRESS_ }; PROPERTY }; _propertyValues = new Object[] { "http://localhost:8080/ Operation op = Operation.newInstance( _qname_operation_ CalculatorApp/CalculatorWSService" }; add, _type_add, _type_addResponse ); } _prepOperation( op ); op.setProperty( Operation.SOAPACTION_URI_PROPERTY, public void _setProperty( String name, Object value ) { "" ); int size = _propertyNames.length; Object resultObj; for (int i = 0; i < size; ++i) { try { if( _propertyNames[i].equals( name )) { resultObj = op.invoke( inputObject ); _propertyValues[i] = value; } catch( JAXRPCException e ) { return; Throwable cause = e.getLinkedCause(); } if( cause instanceof java.rmi.RemoteException ) { } throw (java.rmi.RemoteException) cause; String[] newPropNames = new String[size + 1]; } System.arraycopy(_propertyNames, 0, newPropNames, throw e; 0, size); } _propertyNames = newPropNames; Object[] newPropValues = new Object[size + 1]; return ((Integer )((Object[])resultObj)[0]).intValue(); System.arraycopy(_propertyValues, 0, newPropValues, } 0, size); _propertyValues = newPropValues; protected static final QName _qname_operation_add = new QName( "http://calculator.me.org/", _propertyNames[size] = name; "add" ); _propertyValues[size] = value; protected static final QName _qname_addResponse = new } QName( "http://calculator.me.org/", "addResponse" ); public Object _getProperty(String name) { protected static final QName _qname_add = new QName( for (int i = 0; i < _propertyNames.length; ++i) { "http://calculator.me.org/", "add" ); if (_propertyNames[i].equals(name)) { protected static final Element _type_addResponse; return _propertyValues[i]; protected static final Element _type_add; } static { } _type_addResponse = new Element( _qname_addResponse, if (ENDPOINT_ADDRESS_PROPERTY.equals(name) || _complexType( new Element[] { USERNAME_PROPERTY.equals(name) || new Element( new QName( "", "return" ), PASSWORD_PROPERTY.equals(name)) { Type.INT )}), 1, 1, false ); return null; _type_add = new Element( _qname_add, _complexType( } new Element[] { if (SESSION_MAINTAIN_PROPERTY.equals(name)) { new Element( new QName( "", "i" ), Type.INT ), return new Boolean(false); new Element( new QName( "", "j" ), Type.INT } )}), 1, 1, false ); throw new JAXRPCException("Stub does not recognize } property: " + name); private static ComplexType _complexType( Element[] } elements ) { ComplexType result = new ComplexType(); protected void _prepOperation(Operation op) { result.elements = elements; for (int i = 0; i < _propertyNames.length; ++i) { return result; op.setProperty(_propertyNames[i], _propertyValu } es[i].toString()); } 64 www.sdjournal.org Software Developer s Journal 6/2008 Java Microedition poziomie kończy się definiowanie wspólnej części interfej- su wymiany między klientem a serwerem. W dalszej części Rejestr UDDI należy zdefiniować klasy, które będą wykorzystywały owy interfejs po stronie klienta i serwera. W kliencie tworzy- my połączenie z serwerem wykorzystując metodę de.eno- 2. Wyszukiwane usługi ugh.polish.rmi.RemoteClient.open(). Listing 7 przedstawia poprawne konstrukcję wywołania tej metody. Przyjmuje ona pełną nazwę interfejsu, który zdefiniowaliśmy oraz ad- res serwera wraz ze ścieżką do serwisu RMI. Należy pa- 3. Komunikacja SOAP 1. Rejestracja usługi - opis WSDL miętać aby definiować te parametry bezpośrednio w wy- wołaniu metody a nie przypisując je do zmiennych a na- stępnie przekazywaniu ich do metody jak to jest pokaza- ne na Listingu 8. Serwer usług W przypadku sukcesu nawiązania połączenia z serwe- rem poprzez powyższą metodę klient może wywoływać me- tody serwera poprzez interfejs o nazwie (RMIServer). Wy- Rysunek 4. Web services podstawowe elementy i operacje korzystuje się do tego referencje obiektu, który zwróciła metoda open jak to przedstawia Listing 9. mentować metody read i write, które odpowiednio czyta- W odpowiedzi w tym przypadku zwracany jest obiekt ją ze strumienia oraz zapisują do strumienia. Jeśli jednak klasy z którym należy obchodzić się zupełnie w sposób nie ma potrzeby tworzenia nowych klas, które będą nośni- lokalny. kiem informacji możemy wykorzystać standardowe klasy Ostatnim etapem konstruowania naszego mechanizmu Java Microedition takie jak np. String lub Vector. W zależ- wykorzystującego RMI jest implementacja serwera. Klasa ności o skomplikowania zastosowania mogą one stanowić serwera jest serwletem dziedziczącym po de.enough.po- elementy parametrów metod jak i być zwracane przez me- lish.rmi.RemoteHttpServlet oraz implementująca jednocze- tody serwera. Większość standardowych klas wspiera se- śnie metody naszego wspólnego interfejsu o nazwie RMI- rializację jednak występują pewne ograniczenia w niektó- Server (Listing 10). Jest to część wykonawcza całego me- rych przypadkach. chanizmu i należy zadbać o poprawną konstrukcję zwra- Zalecane jest zapoznanie się przed ich zastosowaniem canych obiektów. w dokumentacji dostępnej na stronie J2MEPolish. Na tym Proces kompilacji oraz budowania serwera wykra- cza poza ramy tego artykułu, która prezentuje tylko za- rys z poziomu języka Java. Jednak dla ułatwienia w kata- Listing 13. Zdalny interfejs serwera logu {j2mepolish-dir}/samples/rmi znajduje się przykłado- public interface CalculatorWSService extends java.rmi.Remote { W Sieci public int add(int i, int j) throws java.rmi.RemoteExc eption; " http://developers.sun.com/mobility/allarticles/#networking " h t t p : // d e ve l o p e r s . s u n . c o m / m o b i l i t y / m i d p / a r t i c l e s/ } socketRMI/ " http://www.j2mepolish.org/cms/leftsection/documentation/ rmirpc.html Listing 14. Przykład wywołania metody serwera " http://kxmlrpc.objectweb.org " http://ksoap.objectweb.org/ public Integer getRemoteResult(int x, int y){ " ht tp : //w w w.netbeans .org /kb / 60 /mobilit y /mobile - dil- bert.html Integer result = null; " http://developers.sun.com/mobility/allarticles/#ws try{ " http://developers.sun.com/mobility/allarticles/#wma " http://ksync.objectweb.org //utworzenie obiektu reprezentujacego serwer " http://developers.sun.com/mobility/midp/articles/syncml/ CalculatorWSService webClient = new " ht tp ://java.sun.com /developer/ Books /J2MEwireless/ CalculatorWSService_Stub(); J2ME12.pdf //wywolanie zdalnej metody " http://developers.sun.com/mobility/midp/articles/jxme/ int z = webClient.add(x, y); " http://developers.sun.com/mobility/allarticles/#wma " http://tavon.org/work/JSON-J2ME result = new Integer(z); " http://www.forum.nokia.com/main/resources/technologies/ }catch (Exception ex) { java/documentation/networking.html " http://developer.sonyericsson.com/wiki/display/leftnav/ System.err.print(ex); Java+Connectivity } " http://developer.motorola.com/docstools/ return result; " http://developer.samsungmobile.com/Developer/index.jsp } Software Developer s Journal 6/2008 www.sdjournal.org 65 Inżynieria oprogramowania larnego HTTP(S). W porównaniu do JSE wersja mobilna jest poddana znacznym restrykcjom: " nie wspiera asynchronicznych wiadomości; " brak załączników w SOAP; " wiadomości w reprezentacji literal (document/literal); " brak wsparcia mapowania typów (brak paczki ja- vax.xml.rpc.encoding); " brak mechanizmu wyszukiwania UDDI. Napisanie MIDletu zintegrowanego z serwerem za pomo- cą SOAP dokonujemy od zlokalizowania adresu url usłu- gi Web Service. W artykule dla ułatwienia zostanie wykorzystany przy- kładowy serwis Calculator pochodzący z pakietu NetBe- Rysunek 5. Carbide.j oraz NeatBeans IDE 6.0 generowanie pnia ans 6.0 (menu File->New Project->Samples->Web Service- >Calculator). wy projekt gdzie został napisany skrypt w ANT budujący Zostaje on uruchomiony na lokalnym serwerze także do- jednocześnie aplikację klienta i serwer. Będzie on stano- łączonym do tego narzędzia. Zatem adres do pliku wsdl jest wił pomocą deskę dla początkujących programistów. Uwa- następujący: http://localhost:8080/CalculatorApp/CalculatorW gę należy zwrócić na proces obfuskacji dokonywany czę- SService?wsdl sto podczas budowania aplikacji. Podczas tego procesu Definicja pliku WSDL dla danego serwisu może wyglądać paczka jar wspólnych klas także zostaje poddana tej opty- tak jak to przedstawia Listing 11. malizacji. W dalszej kolejności przechodzimy do realizacji klienta w Należy pamiętać, że jeśli dokonano obfuskacji to pacz- którym należy zaimplementować: ka jar wspólnych klas musi się znalezć po obu stronach jednocześnie na kliencie jak i na serwerze. Pominięcie te- " stub czyli pień czyli najprościej określając klasę przez go faktu jest sygnalizowane najczęściej wyjątkiem Class którą przechodzą wszystkie żądania i odpowiedzi z not found: a lub Class cast exception wynika to przede serwera; wszystkim ze zmiany nazwy klas po zakończeniu pracy " interfejs serwisu; obfuscatora. " klasę uruchamiającą zdalną metodę Web Service. Web Services SOAP Generowanie klasy pnia można dokonać automatycznie korzy- Simple Object Access Protocol jest standardem wymiany stając z takich narzędzi jak Carbide.j lub Netbeans IDE 6.0, informacji, którego składnia oparta jest na XML. Protokół WTK Sun 2.5. We wszystkich przypadkach po prostu należy SOAP należy do grupy rozwiązań określanych terminem podać adres url do pliku WSDL Web Service jak jest to poka- Web Services. zane na Rysunku 5 . Technologia Web Services zapewnia konstrukcję roz- Więcej informacji na temat tych narzędzi oraz wspo- proszonych komponentów usługowych. W ramach Web mnianego procesu znajduje się na stronach: http://www.j Services oprócz SOAP odpowiedzialnego za zdalne wy- 2mepolish.org/cms/leftsection/documentation/rmirpc.html, http:// woływanie usługi, istnieje jeszcze WSDL oraz UDDI (Ry- kxmlrpc.objectweb.org. W wyniku zautomatyzowanego działa- sunek 4). nia tych narzędzi zostaje wygenerowana klasa pnia (Listing 12) Język opisu interfejsu WSDL (Web Services Descrip- oraz zdalny interfejs serwera (Listing 13). tion Language) służy do dystrybucji usług sieciowych nie- Utworzone w ten sposób klasy po prostu dołączamy do zależnie od jej implementacji. UDDI (Universal Descrip- naszej aplikacji. Przykładowe wywołanie metody serwera tion, Discovery and Integration) ułatwia udostępnianie do- przy zastosowaniu wygenerowanych klas przedstawia Li- kumentów WSDL przez umieszczenie ich w specjalnej ba- sting 14. zie danych. Jak można zaobserwować proces tworzenia klienta oka- Zarejestrowane komponenty usług mogą być następnie zał się w większości automatyczny. Cała technika tworzenia przeszukiwane przez klientów w celu ich wykorzystania. jest przezroczysta dla programisty. Wygenerowane klasy są Wracając do Java Microedition komunikację SOAP moż- bezpośrednio napisane w Java ukrywając szczegóły imple- na zrealizować wykorzystując z jednej strony opcjonalną mentacyjne SOAP. paczkę JSR-172 lub dołączyć do aplikacji bibliotekę jar z implementacją tego protokołu. Specyfikacja JSR-172 Web Podsumowanie Service jest uboższym zbiorem interfejsów pochodzącym Aby ułatwić Czytelnikowi wybór właściwej metody do swoje- od JSE API JAX-RPC(1.1). Dostarcza infrastrukturę Web go zastosowania zostanie przedstawione krótkie zestawienie Services bazująca na synchronicznym modelu zdalnych mocnych i słabych stron opisanych metod. wywołań procedur RPC. Gniazda są szybkim rozwiązaniem w przypadku pro- Oferuje szeroką paletę typów danych oraz komunikację stych wiadomości nie wymagające definiowania rozbudo- poprzez wiele protokołów sieciowych między innymi popu- wanego protokołu. Dostępne są na każdym urządzeniu od 66 www.sdjournal.org Software Developer s Journal 6/2008