background image

58

Inżynieria

oprogramowania

www.sdjournal.org

 Software Developer’s Journal   6/2008

Java Microedition 

– metody integracji aplikacji

A

plikacje  Java  Microedition  (konfiguracji 
CLDC) działające na urządzeniach przeno-
śnych  mają  zazwyczaj  ograniczone  zaso-

by sprzętowe. W większości przypadków limitowa-
na pamięć oraz wolne procesory na urządzeniach 
powodują, że pewne zadania są mało wydajne lub 
niemożliwe do zrealizowania. Rozwiązaniem tego 
problemu może być przeniesienie części funkcjo-
nalności aplikacji na stronę serwera. W ten sposób 
realizuje się ubogiego klienta, który wymaga mniej 
zasobów  przy  zakładanej  funkcjonalności.  Nato-
miast  serwer  przejmuje  wymagające  większych 
zasobów zadania i zwraca do aplikacji klienckiej 
tylko rezultaty swojej pracy. 

Niniejszy  artykuł  skupi  się  na  sposobach  in-

tegracji aplikacji klienta JME z aplikacją serwera. 
Zostanie  zaprezentowany  krótki  przegląd  metod 
za  pomocą  których  można  skomunikować  klien-
ta z serwerem. 

Gruntownie zostaną opisane wybrane mecha-

nizmy integracji, które obecnie są najczęściej sto-
sowane. Na koniec zostanie przedstawiona ocena 
wybranych metod w odniesieniu do wszechstron-
nego zastosowania. 

Uwagi na temat technologii JME

W technologii JME paczki z bibliotekami dostępne 
są bezpośrednio w maszynie wirtualnej KVM (Kilo-
byte
 Virtual Machine) urządzenia dzięki czemu pro-
gramista nie musi martwić się o ich dostępność. Z 
drugiej strony istnieją rozwiązania, które wymagają 
dołączenia odpowiedniej biblioteki zawartej w ar-
chiwum jar. Wiąże się to niestety ze wzrostem roz-
miaru (archiwum jar) aplikacji co ma istotne zna-
czenia w przypadku limitów maksymalnej wielko-
ści narzuconej przez producentów. 

Zagadnienie  to  nie  stanowi  jednakże  tematu 

niniejszego  artykułu.  Zainteresowanym  polecam 
strony producentów urządzeń, gdzie w większości 
przypadków tego rodzaju informacje są dostępne. 

Jednym  z  lepszych  zestawień  danych  technicz-
nych  urządzeń  mobilnych  różnych  producentów 
jest strona J2MEPolish. 

Kolejnym  ważnym  aspektem  jest  profil  urzą-

dzenia Mobile Information Device Profile w skró-

Albert Wachowicz

Albert  Wachowicz  pracuje  na  stanowisku  Software 
Specialist w BLStream wchodzącym w skład Grupy 
BLStream. Grupa BLStream powstała by efektywniej 
wykorzystywać potencjał dwóch, szybko rozwijają-
cych się producentów oprogramowania – BLStream 
i Gamelion. Firmy wchodzące w skład grupy specja-
lizują się w wytwarzaniu oprogramowania dla klien-
tów korporacyjnych, w rozwiązaniach mobilnych oraz 
produkcji i testowaniu gier. 
Kontakt z autorem: albert.wachowicz@gmail.com

Listing 1. 

Przykładowa metoda wysyłająca 

wiadomość poprzez gniazdo

void

 

sendSocketMsg

(

String

 

destAddr

String

 

msg

)

 

{

        

try

 

{

           

 //utworzenie gniazda 

            

SocketConnection

 

client

 

=

 

(

SocketConnection

)

 

Connector

.

open

(

"socket://"

 

+

 

destAddr

 

+

 

":3000"

)

;

           

 //otwarcie strumienii wejscia/wyjscia

            

InputStream

 

is

 

=

 

client

.

openInputStr

eam

()

;

            

OutputStream

 

os

 

=

 

client

.

openOutputSt

ream

()

;

            
           

 // wysłanie wiadomosci doserwera

            

os

.

write

(

msg

.

getBytes

())

;

            

os

.

write

(

'\n'

)

;

             
           

 // odczytanie odpowiedzi (dla 

przykladu do znaku konca 
linii \n lub gdy serwer 
zamknie polaczenie)

            

int

 

c

 

=

 0

;

            

StringBuffer

 

sb

 

=

 

new

 

StringBuffer

()

;

            

while

 

((

c

 

=

 

is

.

read

())

 

!=

 

-

&&

 

(

c

!=

'\

n'

))

 

{

                

sb

.

append

((

char

)

c

)

;

            

}

            
            

System

.

out

.

println

(

"[Serwer]"

+

 

sb

.

toString

())

;

           

 //analiza odpowiedzi...

              
           

 // zamkniecie strumieni oraz 

polaczenia

            

is

.

close

()

;

            

os

.

close

()

;

            

client

.

close

()

;

        

}

 

catch

 

(

IOException

 

ex

)

 

{

            

ex

.

printStackTrace

()

;

        

}

    

}

background image

Java Microedition

59

www.sdjournal.org

Software Developer’s Journal   6/2008

cie MIDP oraz konfiguracja Connected Limited Device Con-
figuration
 CLDC. Określa się w nich zbiór interfejsów i klas 
dostępnych w danej KVM. Aktualnie dominującym profilem 
jest MIDP 2.0 w połączeniu z konfiguracją CLDC 1.1 (lub 
1.0). Jednakże istnieją także urządzenia określane mianem 
wersji MIDP 1.0. W artykule zostanie zaznaczone od któ-
rej wersji profilu dostępny jest dany mechanizm jeśli bę-
dzie to istotne. 

Spis metod integracyjnych

Biorąc pod uwagę ogólne standardy komunikacji siecio-
wej  w  JME  można  wyróżnić  między  innymi  następujące 
mechanizmy:

•   Komunikacja niskopoziomowa – UDP, TCP, TLS(SSL);
•   HTTP/HTTPS;
•   RMI – J2MEPolishRMI, implementacja RMI bazująca na 

gniazdach;

•   XML-RPC – kXMLRPC, J2MEPolishRPC;
•   Web Services – kSOAP[5], klient WebServices w NetBe-

ans, JSR-172;

•   WMA – SMS/MMS;
•   SIP;
•   SyncML – kSync, implementacja SyncML;
•   Peer2Peer – JXTA;
•   Bluetooth;
•   Inne: 

Listing 2. 

Metoda nasłuchujące na TCP

public

 

void

 

wait4TcpConnection

()

 

{

        

try

 

{

           

 // utworzenie serwera nasluchujacego na porcie 

3000

            

ServerSocketConnection

 

server

 

=

 

(

ServerSocketConnection

)

 

Connector

.

open

(

"socket://:3000"

)

;

           

 // oczekiwanie na polaczenia

            

System

.

out

.

println

(

"Oczekiwanie na 

polaczenie..."

)

;

            

SocketConnection

 

client

 

=

 

(

SocketConnection

)

 

server

.

acceptAndOpen

()

;

            

System

.

out

.

println

(

"Zaakceptowanie polaczenia z 

adresu"

 

+

 

client

.

getAddress

())

;

           

 // utworzenie strumienie wejscia/wyjscia

            

InputStream

 

is

 

=

 

client

.

openInputStream

()

;

            

OutputStream

 

os

 

=

 

client

.

openOutputStream

()

;

           

 // czytanie danych ze strumienia (dla przykladu 

do znaku konca linii \n lub gdy 
klient zamknal polaczenie)

            

int

 

c

;

            

StringBuffer

 

sb

 

=

 

new

 

StringBuffer

()

;

            

while

 

(((

c

 

=

 

is

.

read

())

 

!=

 

-

1

)

 

&&

 

(

c

!=

'\n'

))

 

{

               

sb

.

append

((

char

)

 

c

)

;

            

}

            

System

.

out

.

println

(

"[KLIENT]"

 

+

 

sb

.

toString

())

;

           

 //analiza wiadomosci ...

            
           

 //wyslanie odpowiedzi

            

String

 

responseMsg

 

=

 

new

 

String

(

"Odpowiedz 

serwera OK

\n

"

)

;

 

            

os

.

write

(

responseMsg

.

getBytes

())

;

           
           

 // zamkniecie strumienii oraz polaczenia

            

is

.

close

()

;

            

os

.

close

()

;

            

client

.

close

()

;

            

server

.

close

()

;

        

}

 

catch

 

(

IOException

 

ex

)

 

{

            

ex

.

printStackTrace

()

;

        

}

}

Listing 3. 

Przykładowa metoda POST

private

 

void

 

doSendHTTP

(

String

 

request

String

 

url

){

        

String

 

responseStr

 

=

 

null

;

        

try

        

{

           

 //utworzenie polaczenia HTTP i ustawienie 

nagłowka

            

HttpConnection

 

conn

 

=

 

(

HttpConnection

)

Connector

.

open

(

url

)

;

            

conn

.

setRequestMethod

(

HttpConnection

.

POST

)

;

            

conn

.

setRequestProperty

(

"Content-Length"

,

                                    

Integer

.

toString

(

reques

t

.

length

()))

;

           

 //wyslanie wiadomosci                        

            

OutputStream

 

out

 

=

 

conn

.

openOutputStream

()

;

            

int

 

requestLength

 

=

 

request

.

length

()

;

            

for

 

(

int

 

i

 

=

 0

;

 

i

 

<

 

requestLength

;

 

++

i

){

                

out

.

write

(

request

.

charAt

(

i

))

;

            

}

            

InputStream

 

in

 

=

 

conn

.

openInputStream

()

;

            
            

StringBuffer

 

responseBuf

;

                

long

 

length

 

=

 

conn

.

getLength

()

;

            

if

 

(

length

 

>

 0

){

                

responseBuf

 

=

 

new

 

StringBuffer

((

int

)

length

)

;

            

}

            

else

{

                

responseBuf

 

=

 

new

 

StringBuffer

()

;

            

}

           

 //odczytanie odpowiedzi

            

int

 

ch

;

            

while

 

((

ch

 

=

 

in

.

read

())

 

!=

 

-

1

)

            

{

                

responseBuf

.

append

((

char

)

ch

)

;

            

}

            

responseStr

 

=

 

responseBuf

.

toString

()

;

            

System

.

out

.

println

(

responseStr

.

toString

())

;

        

}

catch

 

(

IOException

 

e

){

            

e

.

printStackTrace

()

;

        

}

catch

 

(

SecurityException

 

e

){

            

e

.

printStackTrace

()

;

        

}

    

}

background image

60

Inżynieria

oprogramowania

www.sdjournal.org

 Software Developer’s Journal   6/2008

•  JSON, 
•  Burlap, 
•  JINI, 
•  GASP, 
•  OpenDMTP, 
•  Payment API, 
•  Jsch.

Komunikacja 

niskopoziomowa oraz HTTP

Gniazda są mechanizmem komunikacji definiującym inter-
fejs programowania do wymiany informacji. W Java Micro-
edition (MIDP 2.0) można zrealizować połączenia gniazd 
wykorzystując UDP, TCP i TLS. 

Ogólny szkielet nawiązania połączenia w JME wykony-

wany jest przez następującą metodę fabryki 

Connector.ope-

n("protocol:address;parameters")

 z paczki javax.microedi-

tion.io . Metoda zwraca określony przez parametr 

protocol

 

konkretny obiekt typu 

Connection

. Na Rysunku 1 przedsta-

wiona  jest  hierarchia  interfejsów,  które  mogą  implemen-
tować  obiekty  zwrócone  przez  metodę 

Connector.open()

Przeźroczysty sposób tworzenia połączenia daje uniwer-
salną  metodę,  która  zgłasza  wyjątek 

ConnectionNotFoun-

dException

 w przypadku gdy urządzenie nie implementuje 

określonego połączenia.

TCP 

Transmission Control Protocol jest połączeniowym proto-
kołem zapewniającym niezawodną komunikację strumie-
niową. Ustanawia dwukierunkową współpracę między ho-
stami z możliwością sterowania przepływem (Rysunek 2). 
Kontroluje poprawność oraz kolejność pakietów danych i 
oczekuje potwierdzania ich odbioru. 

W przypadku wystąpienia błędów komunikacyjnych po-

trafi przeprowadzić retransmisję. Protokół TCP nie zobo-
wiązuje się jednak na zrealizowanie połączenia w określo-
nym czasie. 

Przy nadmiernym obciążeniu łącz daje się zaobserwo-

wać nagłe spowolnienie transmisji. Spowodowane to jest 
między innymi błędnymi pakietami i wysyłaniem żądań ich 
ponownej retransmisji.

TCP w Java Microedition realizowane jest poprzez wy-

wołanie metody 

Connector.open

 oraz określenie minimalnie 

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-

dany w parametrach wywołania metody jest zaprezentowa-
ny na Listingu 1.

Natomiast  aplikacja  nasłuchująca  i  wykorzystująca 

gniazda TCP jest przedstawiona na Listingu 2. Została ona 
zrealizowana  w  JME  w  celu  zobrazowania  możliwości  tej 
technologii. Równie dobrze serwer oczekujący na połącze-
nia może być zaimplementowany na dowolnej maszynie ob-
sługującej gniazda TCP.

TLS(SSL)

Transport Layer Security jest protokołem bezpieczeństwa, 
który bazuje na starszym protokole Secure Socket Layer 
(SSL). 

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/
klienta, zapewnianie integralności przesyłanych komuni-
katów. W JME (MIDP 2.0) TLS realizuje się po niewielkich 
modyfikacjach kodu (Rysunek 3).

Bibliografia

•   XML-RPC – zdalne wywoływanie procedur oparte na języku 

XML http://www.xmlrpc.com .

•   WMA – Wireless Messaging API zbiór klas i metod do ob-

sługi komunikacji SMS/MMS 

•   http://java.sun.com/products/wma/index.jsp
•   SIP – Session Initiation Protocol protokół inicjowania sesji
•   http://developers.sun.com/mobility/apis/articles/sip/

Listing 4. 

HttpServlet storna serwera http

public

 

class

 

DeliveryServlet

 

extends

 

HttpServlet

 

{

    

public

 

void

 

init

(

ServletConfig

 

config

)

 

throws

 

ServletException

{

        

super

.

init

(

config

)

;

        ...
    

}

public

 

void

 

doPost

(

HttpServletRequest

 

request

         

HttpServletResponse

 

response

)

        

throws

 

ServletException

IOException

  

{

   

 //odebranie wiadomosci od klienta i analiza jej

    

BufferedReader

 

br

 

=

 

request

.

getReader

()

;

   

String

 

buf

 

=

 

br

.

readLine

()

;

    

System

.

out

.

println

(

buf

)

;

   

 //operacje zwiazane z otrzymanymi danymi ...

   

 //przygotowanie odpowiedzi do klienta i wysłanie jej

    

response

.

setContentType

(

"text/html"

)

;

    

PrintWriter

 

out

 

=

 

response

.

getWriter

()

;

    

out

.

println

(

"Odpowiedz serwera OK"

)

;

    

out

.

close

()

;

  

}

      ...

Listing 5. 

Interfejs Serwera RMI

package pl.awa;

public

 

interface

 

RMIServer

 

extends

 

Remote

 

{

  

 //metoda dodaje uzytkownika

public

 

User

 

addUser

(

 

String

 

name

String

 

password

Address

 

address

)

 

   

throws

 

RemoteException

;

   
  

 //zwraca obiekt klasy Address

   

public

 

Address

 

getAddress

(

 

String

 

userName

 

)

 

   

throws

 

RemoteException

;

   

background image

Java Microedition

61

www.sdjournal.org

Software Developer’s Journal   6/2008

HTTP

Hypertext Transfer Protocol może służyć jako nośnik infor-
macji między MIDletem a serwerem. Określa on formę żą-
dań klienta, które tworzymy między innymi za pomocą po-

leceń GET lub POST. Synchronicznie tworzone są odpo-
wiedzi na żądanie (

response

). 

Ze względu na to że jest to protokół bezstanowy, nie 

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-
ków (FileConnection API) urządzenia aby zapamiętać stan 
transakcji. Standardowo w konfiguracji CLDC wykorzystu-
je się metodę 

Connector.open

 z określeniem protokołu http:

// oraz podaniem adresu serwera. Obiekt typu 

HttpConnec-

tion

 utworzony w ten sposób posiada metodę 

setRequest-

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-

perty(„name”,”value”)

 jak to jest pokazane na przykłado-

wym Listingu 3. 

Właściwa treść wiadomości do serwera jest przesyła-

na strumieniowo analogicznie jak w gniazdach. Kod odpo-
wiedzi serwera otrzymujemy za pomocą metod 

getRespon-

seCode

, która zwraca statyczną wartość typu 

integer

 okre-

ślającą kod (zdefiniowaną w klasie 

HttpConnection

) lub 

ge-

tResponseMessage

 w przypadku gdy istotna jest treść od-

powiedzi.

Natomiast stronę serwera można zrealizować w dowol-

nej technologii obsługującej HTTP. W artykule zostanie po-
kazana implementacja za pomocą Java Servlet. Na Listn-

Listing 6. 

Klasy implementujące interfejs Serializacji

import de.enough.polish.io.Serializable;

public

 

class

 

User

 

implements

 

Serializable

 

{

   

public

 

String

 

name

;

   

public

 

Address

 

address

;

   

public

 

String

 

password

;

}

import de.enough.polish.io.Externalizable;

public

 

class

 

Address

 

implements

 

Externalizable

 

{

   

private

 

String

 

street

;

   

private

 

String

 

city

;

   

public

 

Address

()

 

{

     

 // wymagany konstruktor

   

}

   

public

 

Address

(

String

 

street

String

 

city

){

      

super

()

;

      

this

.

street

 

=

 

new

 

String

(

street

)

;

      

this

.

city

 

=

 

new

 

String

(

city

)

;

   

}

   

public

 

void

 

read

(

DataInputStream

 

in

)

 

throws

 

IOException

 

{

      

this

.

street

 

=

 

in

.

readUTF

()

;

      

this

.

city

 

=

 

in

.

readUTF

()

;

   

}

   

public

 

void

 

write

(

DataOutputStream

 

out

)

 

throws

 

IOException

 

{

      

out

.

writeUTF

(

 

this

.

street

 

)

;

      

out

.

writeUTF

(

 

this

.

city

 

)

;

   

}

}

Listing 7. 

Poprawne wywołanie metody RemoteClient.open

this

.

server

 

=

 

(

RMIServer

)

 

RemoteClient

.

open

(

   

"pl.awa.RMIServer"

   

"http://localhost:8080/awa/myservice"

 

)

;

Listing 8. 

Błędne wywołanie metody RemoteClient.open

String

 

myInterfaceName

 

=

 

"pl.awa.RMIServer"

;

this

.

server

 

=

 

(

RMIServer

)

 

RemoteClient

.

open

(

   

myInterfaceName

   

"http://localhost:8080/awa/myservice"

 

)

;

Listing 9. 

Zdalne wywołanie metody serwera

//...

User

 

user

 

=

 

this

.

server

.

addUser

(

 

name

password

address

)

;

System

.

out

.

println

(

"Name:"

 

+

 

user

.

name

.

toString

())

;

//...

Listing 10. 

Implementacja serwera RMI

import de.enough.polish.rmi.RemoteException;
import de.enough.polish.rmi.RemoteHttpServlet;

public

 

class

 

GameServerImpl

 

extends

 

RemoteHttpServlet

 

implements

 

GameServer

{

   

public

 

User

 

addUser

(

 

String

 

name

String

 

password

Address

 

address

)

   

throws

 

RemoteException

{

     

 //...

     

 //utworzenie obiektu klasy User ktory bedzie zwrocony 

przez metode

      

User

 

user

 

=

 

new

 

User

()

;

      

user

.

name

 

=

 

name

;

      

user

.

password

 

=

 

password

;

      

user

.

address

 

=

 

address

;

     

 //...

      

return

 

user

;

   

}

   

public

 

Address

 

getAddress

(

 

String

 

userName

 

)

 

throws

 

RemoteException

{

     

 //...

     

 //wyszukiwanie uzytkownika po nazwie z bazy danych

      

String

 

street

 

=

 

getStreetFromDB

(

userName

)

;

      

String

 

city

 

=

 

getCityFromDB

(

userName

)

;

      

Address

 

address

 

=

 

new

 

Address

(

street

city

)

;

     

 //...

      

return

 

address

;

   

}

}

background image

62

Inżynieria

oprogramowania

www.sdjournal.org

 Software Developer’s Journal   6/2008

gu  4  wykorzystany  zostaje  interfejs 

HttpServlet

  oraz  im-

plementacja dwóch jego metod 

doGET

 oraz 

doPOST

, które re-

alizują określone w nazwie żądania i generują odpowiedzi 
do klienta. Aplikację serwerową można uruchomić na kon-
tenerze wspierającym serwety jak np. Apache Tomcat lub 
JBoss.  Odnośnie  wykorzystania  HTTPS  dostępnego  od 
MIDP 2.0 to analogia użycia jest podobna jak w przypad-
ku TLS.

RMI

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-
ka ta umożliwia zdalne wywołania metod (RPC) obiektów w ję-
zyku Java w przeźroczysty sposób dla programisty ukrywając 
niskopoziomowe szczegóły związane z obsługą protokołów. 

Cała idea opiera się na traktowaniu obiektów zdalnych 

w ten sam sposób jak tych działających lokalnie. W konfi-
guracji CLDC JME standardowo nie można spotkać biblio-
teki odpowiedzialnej za obsługę RMI. Istnieje gotowe roz-
wiązanie opcjonalne J2MEPolish RMI, które jest dostęp-
ne w dwóch licencjach GPL oraz w komercyjnej Commer-
cial License. 

Za pomocą tego narzędzia można w przyjemy i szybki 

sposób zaimplementować mobilnego klienta komunikujące-
go się ze zdalnym serwerem. Wszystkie wywołania metod 

Rysunek 1. 

Hierarchia interfejsów komunikacji w JME

CLDC Generic Connection

Framework Interfaces

SockedConnection

HttpConnection (MIDP 1.0)

SecureConnection

MIDP

 2.0

Interfaces

Connection

DatagramConnection

OutputConnection

InputConnection

StreamConnection

StreamConnectionNotifier

ServerSockedConnection

UDPDatagramConnection

ContentConnection (MIDP 1.0)

Rysunek 2. 

Komunikacja poprzez gniazda TCP

Klient

Serwer

Serwer

MIDIet

TCP

TCP

IP

IP

Strumień danych lub

komunikaty aplikacji

Pakiety

Pakiety IP

Rysunek 3. 

Transformacja na gniazda bezpieczeństwa TLS

TCP

TLS (SSL)

SocketConnection socketConnection =

(SocketConnection) Connection.open("socket://hostName:5000");

socketConnection.setSocketOption(SocketConnection.DELAY, 0);

       OutputSteream os = socketConnection.openOutputStream());

SecureConnection socketConnection =

(SecureConnection) Connector.open("ssl://hostName:5001");

String secureProtocolName =

secureConnection.getSecurityInfo().getProtocolName();

OutputSteream os = secureConnection.openOutputSteream();

background image

63

Java Microedition

www.sdjournal.org

Software Developer’s Journal   6/2008

oraz dostępność obiektów dają wrażenie ich lokalnego wy-
stępowania po stronie klienta i vice versa. Aby to uzmysło-
wić drogiemu Czytelnikowi w dalszej części zostaną przed-
stawione kolejne kroki jakie należy podjąć aby to zrealizo-
wać.

Na pierwszym etapie definiujemy zdalny interfejs, który bę-

dzie wspólnym protokołem między klientem a serwerem. Okre-
śla się w nim metody, które będzie udostępniał serwer. Jak 
można zauważyć na Listingu 5 wymagane jest aby dziedziczył 
on po interfejsie 

de.enough.polish.rmi.Remote

 oraz każda me-

toda musi mieć możliwość zgłaszania wyjątku 

de.enough.po-

lish.rmi.RemoteException

.

W kolejnym kroku należy zdefiniować zbiór klas, któ-

re będą wspólne dla obydwu stron. Klasy te muszą imple-
mentować interfejsy serializacji 

de.enough.polish.io.Seria-

lizable

 lub de.enough.polish.io.Externalizable. Serializacja 

w najprostszym określeniu polega na procesie przekształ-
cenia obiektów do postaci strumienia bajtów. Dzięki zasto-
sowaniu tego mechanizmu w RMI można przesyłać obiek-
ty poprzez strumieniowy protokół sieciowy. W klasie imple-
mentującej Serializable programista nie musi martwić się 
o proces serializacji dokonuje się on automatycznie jak to 
widać na Listingu 6. Natomiast różnica w klasie implemen-
tującej 

Externalizable

 wynika z tego że należy zaimple-

Listing 11. 

Przykładowy plik WSDL usługi Wheather

<

?xml version=

"1.0"

 

encoding=

"UTF-8"

?

>

<!-- Published by JAX-WS RI at http://jax-ws.dev.java.net. RI's version is JAX-WS RI 

2.1.2_01-hudson-189-. --><!-- Generated by JAX-WS RI at http://jax-ws.dev.java.net. RI's version is 
JAX-WS RI 2.1.2_01-hudson-189-. -->

<

definitions xmlns:wsu=

"http://docs.oasis-open.org/wss/2004/01/oasis-

200401-wss-wssecurity-utility-1.0.xsd"

 

xmlns:wsp=

"http://schemas.xmlsoap.org/ws/2004/09/policy"

 

xmlns:

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"

>

<

types

>

<

xsd:schema

>

<

xsd:import namespace=

"http://calculator.me.org/"

 

schemaLocation=

"http://localhost:8080/CalculatorApp/CalculatorWSService?xsd=

1"

><

/xsd:import

>

<

/xsd:schema

>

<

/types

>

<

message name=

"add"

>

<

part name=

"parameters"

 

element=

"tns:add"

><

/part

>

<

/message

>

<

message name=

"addResponse"

>

<

part name=

"parameters"

 

element=

"tns:addResponse"

><

/part

>

<

/message

>

<

portType name=

"CalculatorWS"

>

<

operation name=

"add"

>

<

input message=

"tns:add"

><

/input

>

<

output message=

"tns:addResponse"

><

/output

>

<

/operation

>

<

/portType

>

<

binding name=

"CalculatorWSPortBinding"

 

type=

"tns:CalculatorWS"

>

<

soap:binding transport=

"http://schemas.xmlsoap.org/soap/http"

 

style=

"document"

><

/soap:binding

>

<

operation name=

"add"

>

<

soap:operation soapAction=

""

><

/soap:operation

>

<

input

>

<

soap:body use=

"literal"

><

/soap:body

>

<

/input

>

<

output

>

<

soap:body use=

"literal"

><

/soap:body

>

<

/output

>

<

/operation

>

<

/binding

>

<

service name=

"CalculatorWSService"

>

<

port name=

"CalculatorWSPort"

 

binding=

"tns:CalculatorWSPortBinding"

>

<

soap:address location=

"http://localhost:8080/CalculatorApp/CalculatorWSService"

><

/soap:address

>

<

/port

>

<

/service

>

<

/definitions

>

background image

64

Inżynieria

oprogramowania

www.sdjournal.org

 Software Developer’s Journal   6/2008

Listing 12. 

Klasa pnia wygenerowany automatycznie

public

 

class

 

CalculatorWSService_Stub

 

implements

 

CalculatorWSService

javax

.

xml

.

rpc

.

Stub

 

{

    
    

private

 

String

[]

 

_propertyNames

;

    

private

 

Object

[]

 

_propertyValues

;

    
    

public

 

CalculatorWSService_Stub

()

 

{

       

_propertyNames

 

=

 

new

 

String

[]

 

{

 

ENDPOINT_ADDRESS_

PROPERTY

 

}

;

       

_propertyValues

 

=

 

new

 

Object

[]

 

{

 

"http://localhost:8080/

CalculatorApp/CalculatorWSService"

 

}

;

    

}

    
    

public

 

void

 

_setProperty

(

 

String

 

name

Object

 

value

 

)

 

{

        

int

 

size

 

=

 

_propertyNames

.

length

;

        

for

 

(

int

 

i

 

=

 0

;

 

i

 

<

 

size

;

 

++

i

)

 

{

            

if

(

 

_propertyNames

[

i

]

.

equals

(

 

name

 

))

 

{

                

_propertyValues

[

i

]

 

=

 

value

;

                

return

;

            

}

        

}

        

String

[]

 

newPropNames

 

=

 

new

 

String

[

size

 

+

 1

]

;

        

System

.

arraycopy

(

_propertyNames

, 0, 

newPropNames

0, 

size

)

;

        

_propertyNames

 

=

 

newPropNames

;

        

Object

[]

 

newPropValues

 

=

 

new

 

Object

[

size

 

+

 1

]

;

        

System

.

arraycopy

(

_propertyValues

, 0, 

newPropValues

0, 

size

)

;

        

_propertyValues

 

=

 

newPropValues

;

        
        

_propertyNames

[

size

]

 

=

 

name

;

        

_propertyValues

[

size

]

 

=

 

value

;

    

}

    
    

public

 

Object

 

_getProperty

(

String

 

name

)

 

{

        

for

 

(

int

 

i

 

=

 0

;

 

i

 

<

 

_propertyNames

.

length

;

 

++

i

)

 

{

            

if

 

(

_propertyNames

[

i

]

.

equals

(

name

))

 

{

                

return

 

_propertyValues

[

i

]

;

            

}

        

}

        

if

 

(

ENDPOINT_ADDRESS_PROPERTY

.

equals

(

name

)

 

||

 

USERNAME_PROPERTY

.

equals

(

name

)

 

||

 

PASSWORD_PROPERTY

.

equals

(

name

))

 

{

            

return

 

null

;

        

}

        

if

 

(

SESSION_MAINTAIN_PROPERTY

.

equals

(

name

))

 

{

            

return

 

new

 

Boolean

(

false

)

;

        

}

        

throw

 

new

 

JAXRPCException

(

"Stub does not recognize 

property: "

 

+

 

name

)

;

    

}

    
    

protected

 

void

 

_prepOperation

(

Operation

 

op

)

 

{

        

for

 

(

int

 

i

 

=

 0

;

 

i

 

<

 

_propertyNames

.

length

;

 

++

i

)

 

{

            

op

.

setProperty

(

_propertyNames

[

i

]

_propertyValu

es

[

i

]

.

toString

())

;

        

}

    

}

    
    

public

 

int

 

add

(

int

 

i

int

 

j

)

 

throws

 

java

.

rmi

.

RemoteException

 

{

        

Object

 

inputObject

[]

 

=

 

new

 

Object

[]

 

{

            

new

 

Integer

(

i

)

,

            

new

 

Integer

(

j

)

        

}

;

        
        

Operation

 

op

 

=

 

Operation

.

newInstance

(

 

_qname_operation_

add

_type_add

_type_addResponse

 

)

;

        

_prepOperation

(

 

op

 

)

;

        

op

.

setProperty

(

 

Operation

.

SOAPACTION_URI_PROPERTY

""

 

)

;

        

Object

 

resultObj

;

        

try

 

{

            

resultObj

 

=

 

op

.

invoke

(

 

inputObject

 

)

;

        

}

 

catch

(

 

JAXRPCException

 

e

 

)

 

{

            

Throwable

 

cause

 

=

 

e

.

getLinkedCause

()

;

            

if

(

 

cause

 

instanceof

 

java

.

rmi

.

RemoteException

 

)

 

{

                

throw

 

(

java

.

rmi

.

RemoteException

)

 

cause

;

            

}

            

throw

 

e

;

        

}

        
        

return

 

((

Integer

 

)((

Object

[])

resultObj

)[

0

])

.

intValue

()

;

    

}

    
    

protected

 

static

 

final

 

QName

 

_qname_operation_add

 

=

 

new

 

QName

(

 

"http://calculator.me.org/"

"add"

 

)

;

    

protected

 

static

 

final

 

QName

 

_qname_addResponse

 

=

 

new

 

QName

(

 

"http://calculator.me.org/"

"addResponse"

 

)

;

    

protected

 

static

 

final

 

QName

 

_qname_add

 

=

 

new

 

QName

(

 

"http://calculator.me.org/"

"add"

 

)

;

    

protected

 

static

 

final

 

Element

 

_type_addResponse

;

    

protected

 

static

 

final

 

Element

 

_type_add

;

    

static

 

{

        

_type_addResponse

 

=

 

new

 

Element

(

 

_qname_addResponse

_complexType

(

 

new

 

Element

[]

 

{

            

new

 

Element

(

 

new

 

QName

(

 

""

"return"

 

)

Type

.

INT

 

)})

, 1, 1, 

false

 

)

;

        

_type_add

 

=

 

new

 

Element

(

 

_qname_add

_complexType

(

 

new

 

Element

[]

 

{

            

new

 

Element

(

 

new

 

QName

(

 

""

"i"

 

)

Type

.

INT

 

)

,

            

new

 

Element

(

 

new

 

QName

(

 

""

"j"

 

)

Type

.

INT

 

)})

, 1, 1, 

false

 

)

;

    

}

    

private

 

static

 

ComplexType

 

_complexType

(

 

Element

[]

 

elements

 

)

 

{

        

ComplexType

 

result

 

=

 

new

 

ComplexType

()

;

        

result

.

elements

 

=

 

elements

;

        

return

 

result

;

    

}

}

background image

65

Java Microedition

www.sdjournal.org

Software Developer’s Journal   6/2008

mentować metody 

read

 i 

write

, które odpowiednio czyta-

ją ze strumienia oraz zapisują do strumienia. Jeśli jednak 
nie ma potrzeby tworzenia nowych klas, które będą nośni-
kiem informacji możemy wykorzystać standardowe klasy 
Java Microedition takie jak np. 

String

 lub 

Vector

. W zależ-

ności o skomplikowania zastosowania mogą one stanowić 
elementy parametrów metod jak i być zwracane przez me-
tody serwera. Większość standardowych klas wspiera se-
rializację jednak występują pewne ograniczenia w niektó-
rych przypadkach. 

Zalecane jest zapoznanie się przed ich zastosowaniem 

w dokumentacji dostępnej na stronie J2MEPolish. Na tym 

poziomie kończy się definiowanie wspólnej części interfej-
su wymiany między klientem a serwerem. W dalszej części 
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-

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-
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.

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-
korzystuje się do tego referencje obiektu, który zwróciła 
metoda open jak to przedstawia Listing 9.

W odpowiedzi w tym przypadku zwracany jest obiekt 

klasy z którym należy obchodzić się zupełnie w sposób 
lokalny. 

Ostatnim etapem konstruowania naszego mechanizmu 

wykorzystującego RMI jest implementacja serwera. Klasa 
serwera jest serwletem dziedziczącym po de.enough.po-
lish.rmi.RemoteHttpServlet
 oraz implementująca jednocze-
śnie metody naszego wspólnego interfejsu o nazwie RMI-
Server (Listing 10). Jest to część wykonawcza całego me-
chanizmu i należy zadbać o poprawną konstrukcję zwra-
canych obiektów.

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-
logu {j2mepolish-dir}/samples/rmi znajduje się przykłado-

Listing 13. 

Zdalny interfejs serwera

public

 

interface

 

CalculatorWSService

 

extends

 

java

.

rmi

.

Remote

 

{

    

public

 

int

 

add

(

int

 

i

int

 

j

)

 

throws

 

java

.

rmi

.

RemoteExc

eption

;

    

}

Listing 14. 

Przykład wywołania metody serwera

public

 

Integer

 

getRemoteResult

(

int

 

x

int

 

y

){

        

Integer

 

result

 

=

 

null

;

        

try

{

     

 //utworzenie obiektu reprezentujacego serwer

            

CalculatorWSService

 

webClient

 

=

 

new

 

CalculatorWSService_Stub

()

;

        

 //wywolanie zdalnej metody

            

int

 

z

 

=

 

webClient

.

add

(

x

y

)

;

            
      

result

 

=

 

new

 

Integer

(

z

)

;

        

}

catch

 

(

Exception

 

ex

)

 

{

            

System

.

err

.

print

(

ex

)

;

        

}

        

return

 

result

;

    

}

Rysunek 4. 

Web services podstawowe elementy i operacje

Serwer usług

Rejestr UDDI

3. Komunikacja SOAP

1. Rejestracja usługi - opis WSDL

2. Wyszukiwane usługi

W Sieci

•   http://developers.sun.com/mobility/allarticles/#networking
•   h t t p : / / d e v e 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

•   http://kxmlrpc.objectweb.org
•   http://ksoap.objectweb.org/
•   ht tp : //w w w.netbeans .org /kb / 60 /mobilit y /mobile - dil-

bert.html

•   http://developers.sun.com/mobility/allarticles/#ws
•   http://developers.sun.com/mobility/allarticles/#wma
•   http://ksync.objectweb.org
•   http://developers.sun.com/mobility/midp/articles/syncml/
•   ht tp : //java .sun.com /developer/ Books /J2MEwireless/

J2ME12.pdf

•   http://developers.sun.com/mobility/midp/articles/jxme/
•   http://developers.sun.com/mobility/allarticles/#wma
•   http://tavon.org/work/JSON-J2ME
•   http://www.forum.nokia.com/main/resources/technologies/

java/documentation/networking.html

•   http://developer.sonyericsson.com/wiki/display/leftnav/

Java+Connectivity

•   http://developer.motorola.com/docstools/
•   http://developer.samsungmobile.com/Developer/index.jsp

background image

66

Inżynieria

oprogramowania

www.sdjournal.org

 Software Developer’s Journal   6/2008

wy projekt gdzie został napisany skrypt w ANT budujący 
jednocześnie aplikację klienta i serwer. Będzie on stano-
wił pomocą deskę dla początkujących programistów. Uwa-
gę należy zwrócić na proces obfuskacji dokonywany czę-
sto podczas budowania aplikacji. Podczas tego procesu 
paczka jar wspólnych klas także zostaje poddana tej opty-
malizacji. 

Należy pamiętać, że jeśli dokonano obfuskacji to pacz-

ka jar wspólnych klas musi się znaleźć po obu stronach 
jednocześnie na kliencie jak i na serwerze. Pominięcie te-
go faktu jest sygnalizowane najczęściej wyjątkiem Class 
not  found:  a
  lub  Class  cast  exception  wynika  to  przede 
wszystkim  ze  zmiany  nazwy  klas  po  zakończeniu  pracy 
obfuscatora.

Web Services SOAP

Simple Object Access Protocol jest standardem wymiany 
informacji, którego składnia oparta jest na XML. Protokół 
SOAP należy do grupy rozwiązań określanych terminem 
Web Services. 

Technologia Web Services zapewnia konstrukcję roz-

proszonych  komponentów  usługowych.  W  ramach  Web 
Services oprócz SOAP odpowiedzialnego za zdalne wy-
woływanie usługi, istnieje jeszcze WSDL oraz UDDI (Ry-
sunek 4). 

Język opisu interfejsu WSDL (Web Services Descrip-

tion Language) służy do dystrybucji usług sieciowych nie-
zależnie  od  jej  implementacji.  UDDI  (Universal  Descrip-
tion, Discovery and Integration
) ułatwia udostępnianie do-
kumentów WSDL przez umieszczenie ich w specjalnej ba-
zie danych. 

Zarejestrowane komponenty usług mogą być następnie 

przeszukiwane przez klientów w celu ich wykorzystania.

Wracając do Java Microedition komunikację SOAP moż-

na zrealizować wykorzystując z jednej strony opcjonalną 
paczkę JSR-172 lub dołączyć do aplikacji bibliotekę jar z 
implementacją tego protokołu. Specyfikacja JSR-172 Web 
Service jest uboższym zbiorem interfejsów pochodzącym 
od JSE API JAX-RPC(1.1). Dostarcza infrastrukturę Web 
Services  bazująca  na  synchronicznym  modelu  zdalnych 
wywołań procedur RPC. 

Oferuje szeroką paletę typów danych oraz komunikację 

poprzez wiele protokołów sieciowych między innymi popu-

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-
ans 6.0 (menu File->New Project->Samples->Web Service-
>Calculator
). 

Zostaje on uruchomiony na lokalnym serwerze także do-

łączonym do tego narzędzia. Zatem adres do pliku wsdl jest 
następujący: http://localhost:8080/CalculatorApp/CalculatorW
SService?wsdl 

Definicja pliku WSDL dla danego serwisu może wyglądać 

tak jak to przedstawia Listing 11.

W dalszej kolejności przechodzimy do realizacji klienta w 

którym należy zaimplementować:

•   stub czyli pień czyli najprościej określając klasę przez 

którą  przechodzą  wszystkie  żądania  i  odpowiedzi  z 
serwera;

•   interfejs serwisu;
•   klasę uruchamiającą zdalną metodę Web Service.

Generowanie klasy pnia można dokonać automatycznie korzy-
stając z takich narzędzi jak Carbide.j lub Netbeans IDE 6.0, 
WTK Sun 2.5. We wszystkich przypadkach po prostu należy 
podać adres url do pliku WSDL Web Service jak jest to poka-
zane na Rysunku 5 . 

Więcej  informacji  na  temat  tych  narzędzi  oraz  wspo-

mnianego  procesu  znajduje  się  na  stronach:  http://www.j
2mepolish.org/cms/leftsection/documentation/rmirpc.html
http://
kxmlrpc.objectweb.org.
 W wyniku zautomatyzowanego działa-
nia tych narzędzi zostaje wygenerowana klasa pnia (Listing 12) 
oraz zdalny interfejs serwera (Listing 13). 

Utworzone w ten sposób klasy po prostu dołączamy do 

naszej aplikacji. Przykładowe wywołanie metody serwera 
przy zastosowaniu wygenerowanych klas przedstawia Li-
sting 14.

Jak można zaobserwować proces tworzenia klienta oka-

zał się w większości automatyczny. Cała technika tworzenia 
jest przeźroczysta dla programisty. Wygenerowane klasy są 
bezpośrednio napisane w Java ukrywając szczegóły imple-
mentacyjne SOAP.

Podsumowanie

Aby ułatwić Czytelnikowi wybór właściwej metody do swoje-
go zastosowania zostanie przedstawione krótkie zestawienie 
mocnych i słabych stron opisanych metod.

Gniazda  są  szybkim  rozwiązaniem  w  przypadku  pro-

stych  wiadomości  nie  wymagające  definiowania  rozbudo-
wanego protokołu. Dostępne są na każdym urządzeniu od 

Rysunek 5. 

Carbide.j oraz NeatBeans IDE 6.0 generowanie 

pnia