Java EE 6 ja RESTful Web Services Osa 3

Artikkelisarjassa olen käynyt läpi Java EE 6 REST palvelun tekemistä JAX-RS rajapinnan avulla. GET-tyyppisiä palveluita on helppoa testata vaikkapa selaimella, koska ne lähettävät GET pyyntöjä. Jos haluat käyttää muitakin REST metodeita, tarvitaan hieman fiksumpi client ohjelma.

REST metodeita käytetään tämän periaatteen mukaan:

HTTP PUT == luo uusi resurssi-instanssi
HTTP GET == hae resurssin tiedot (yhden tai useamman)
HTTP POST == päivitä resurssia
HTTP DELETE == poista resurssi

Testaukseen ja muuhunkin käyttöön voit tehdä Java-pohjaisen asiakasohjelman helposti, käyttäen URLConnection luokkaa. Tässä esimerkkikoodia:

// REST PUT CLIENT SAMPLE
String xmlMessage = ”<?xml version=\”1.0\” encoding=\”UTF-8\”?><root>test xml</root>”; // create sample xml file or load it
URL u = new URL(”http://www.myserver.fi/services/res/addmore&#8221;); // set up URL
HttpURLConnection uc = (HttpURLConnection) u.openConnection();
uc.setRequestMethod(”PUT”);
uc.setRequestProperty(”Content-type”, ”application/xml”);
uc.setDoOutput(true);
OutputStream os = uc.getOutputStream();
PrintWriter pw = new PrintWriter(os);
pw.println(xmlMessage);
pw.close();
uc.connect();
InputStream is = uc.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
while (br.ready()) {
System.out.println(br.readLine());
}
br.close();
uc.disconnect();

Huomaa yllä pari pientä yksityiskohtaa:

Ota käyttöön nimenomaan HttpURLConnection, koska sen kautta aukeaa muutama tärkeä lisämetodi. Kuten yllä näet, aseta HTTP request method haluttuun moodiin, tässä siis PUT. Aseta content-type parameteri muotoon application/xml – niin kohdepalvelin ymmärtää saavansa XML sanoman. Aseta output-arvo todeksi, niin myös URL:iin lähettäminen onnistuu. Sitten vain kirjoitat xml-sanoman url osoitteeseen, ja luet mitä palvelin lähettää takaisin.

Jos haluat https-salauksen päälle, yksinkertaisesti vaihdat URL:ksi https – java sisältää tuen ssl salaukselle palvelimen kanssa (jos palvelimesta toiminto löytyy). Jos haluat käyttää BASIC autentikointia, voit tehdä sen esim. näin (ennen yhteyden avaamista):

//  write auth header
BASE64Encoder encoder = new BASE64Encoder();
String encodedCredential = encoder.encode( (username + ”:” + password).getBytes() );
connection.setRequestProperty(”Authorization”, ”BASIC ” + encodedCredential);

Kuten näet, REST on melkolailla yksinkertainen ’low tech’ ratkaisu ja näin ollen vastalause SOAP:n monimutkaisuudelle. Huomaa että ylläolevat koodit toimivat pienin muutoksin myös esim. Android alustalla, ja näillä voit kytkeytyä esim. Flickr, Ebay, Amazon, Facebook tai Google REST palveluihin.

Palataan taas takaisin palvelinpäähän ja Java EE 6 uutuuksiin ensi osassa.

Java EE 6 ja RESTful Web Services Osa 2

Artikkelisarjan ensimmäisessä osassa tutustuttiin REST web service tyyliin uudella Java EE 6 alustalla. Ensimmäinen osa esitteli tavanomaisen Hello World tasoisen esimerkin jossa palautettiin ääri-yksinkertainen XML tiedosto. Entäpä miten REST taipuu monimutkaisempiin tarpeisiin kuten esim. tuotedokumentin palauttamiseen?

Parasta on hyödyntää JAXB tekniikkaa. Sensijaan että paluutyypiksi laitetaan String, kannattaakin tehdä XML schema joka määrittää haluamasi XML siirtorakenteen, generoida siitä Java-tyypit, ja käyttää näitä Java-tyyppejä paluuarvoina. Tässä esimerkki suht yksinkertaisesta tuoteschemasta:

<?xml version=”1.0″ encoding=”UTF-8″?>
<xs:schema xmlns:xs=”http://www.w3.org/2001/XMLSchema&#8221; elementFormDefault=”qualified” attributeFormDefault=”unqualified”>
<xs:element name=”items”>
<xs:complexType>
<xs:sequence>
<xs:element name=”item” type=”item” maxOccurs=”unbounded”/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:complexType name=”item”>
<xs:sequence>
<xs:element name=”id” type=”xs:int”/>
<xs:element name=”name” type=”xs:string”/>
<xs:element name=”description” type=”xs:string”/>
<xs:element name=”price” type=”xs:string”/>
<xs:element name=”publishDate” type=”xs:date”/>
</xs:sequence>
</xs:complexType>
</xs:schema>

Yllä on tyypillinen xml rakenne jossa on juurielementti, jonka alla on alielementtejä, joiden alla on elementteihin koodattua tietoa. Anna tämä schema JAXB työkalulle ja siitä generoituu luokat Items, Item (sekä ObjectFactory). Tämän jälkeen voit koodata REST web service metodin tähän tapaan:

@GET

public Items getItem(@QueryParam(value = ”id”) int id) {

// here we would query database for item id 1 but

// to make the point we instead just generate same item

// everytime and ignore the parameter

Items items = new Items();

Item item = new Item();

item.setId(1);

item.setName(”Lawnmower”);

item.setDescription(”Useful tool to keep your lawn tidy”);

item.setPrice(”1000 eur”);

items.getItem().add(item);

return items;

}

Kun kutsut tätä koodia selaimesta osoitteella http://localhost:21136/RESTDemo/resources/items?name=Arto – saat tämän näköisen XML dokumentin palkaksesi:

<?xml version=”1.0″ encoding=”UTF-8″ standalone=”yes”>
<items>
<item>
<id>1</id>
<name>Lawnmower</name>
<description>Useful tool to keep your lawn tidy</description>
<price>1000 eur</price>
</item>
</items>

Mielenkiintoista? REST hyväksyy paluuarvoiksi myös JAXB mäpätyn objektin – koska se tietää miten se muutetaan XML muotoon. Ylläoleva esimerkki luo itse Items ja Item oliot, mutta aito koodi tietysti pujahtaa tietokantaan hakemaan nämä – esim. kätevästi JPA rajapintaa ja Entity Object:eja hyödyntäen.

Suosittelen lähtemään liikkeelle Schema määrityksestä – koska silloin ratkaisu tulee olemaan XML schema ehdoilla tehty, ei Java olioiden ehdoilla. Schemat ja XML rajapinnat ovat kuitenkin jotain jonka kanssa tulet luultavasti elämään pitkänkin aikaa – sitävastoin oliorakenteet ja palvelimen sisäinen logiikka voi vapaasti elää ja hengittää.

Debugging: Voit saada ilmoituksen tähän tapaan:

SEVERE: A message body writer for Java type, class fi.tieturi.services.hello.Item2, and MIME media type, application/xml, was not found

Tämä tarkoittaa että JAXB annotaatioita ei ole luokkaan tehty eli yrität palauttaa olion joka ei ole JAXB mäpätty ja näin ei ole tietoa miten se saadaan XML muotoon. Huomaa myös että voit palauttaa vain schema päätason olion eli juurielementin, et sisäisiä pikkutyyppejä joita ei ole suunniteltu päätasolla käytettäviksi.

Muita mielenkiintoisia piirteitä:

Huomaa että tehdessäsi lisää metodeita luokkaan voit määrittää aina path osuuden uudestaan. Metoditason path määritys käytännössä lisätään luokkatason path määritykseen kun haet resurssia. Jos haluat esim. uuden metodin tähän tapaan:

@GET

@Path(”item2”)

public Items getItem2() {

Items items = new Items();

return items;

}

Voit kutsua sitä selaimesta tällaisella URL osoitteella: http://localhost:21136/RESTDemo/resources/items/item2

Näin on mahdollista tehdä samaan REST palveluun useitakin eri hakumetodeita, esim. hae kaikki tuotteet, hae yksi tuote, jne.

Toinen mielenkiintoinen jippo mitä voit tehdä on siirtyä XML koodauksesta JSON koodaukseen. JSON tulee sanoista JavaScript Object Notation ja on XML:ää hitusen tehokkaampi ja suoraviivaisempi tapa siirtää olioita verkon yli. Java EE 6 Rest antaa mahdollisuuden muuttaa luokan tai yksittäisen metodin koodaustapa JSON muotoon tähän tapaan:

@Produces(MediaType.APPLICATION_JSON)

Paluutyyppien tulee silti edelleen olla JAXB mäpättyjä objekteja. Nyt osassa 1 esitellyn metodin palauttama dokumentti olisikin xml:n sijaan tällainen:

{”item”:{”id”:”1″,”name”:”Lawnmower”,”description”:”Useful tool to keep your lawn tidy”,”price”:”1000 eur”}}

Siinä on edelleen sama data, mutta nyt tiiviimmässä paketissa. Huomaa että koska annotaatio voidaan tehdä metoditasolla, voit halutessasi tarjota samoista metodeista erikseen sekä XML että JSON vaihtoehdot sen mukaan mitä asiakaspää osaa helpoimmin käsitellä.

Tässä taas hetkeksi sulateltavaa, palaamme taas asiaan jatkossa.