JSON serialisointi: Pois Circular Reference – manalasta

En tiedä onko tuttu tilanne, mutta itselleni harmillisen usein tavattu. Otetaan oliorakenne, esim. Order -> OrderItem, eli tilaus ja tilausrivejä. Rakenne menisi näin:

class Order {

  List<OrderItem> orderItems;

}

class OrderItem {}

Tähän asti kaikki loistavasti. Nyt kuitenkin on useita syitä miksi haluaisimme myös linkin OrderItemistä Order-luokkaan, esim. jos item-riveissä on assosiaatioita muuallekin ja niistä pitäisi näppärästi päästä header-tietoihin kiinni. Tai jos käyttää serialisointiin JPA-tekniikkaa eikä halua tehdä turhia välitauluja (One-to-Many assosiaatiossa tieto assosiaatiosta on many-päässä eli OrderItem luokassa)

Pysyitkö mukana? Hyvä, muutamme siis rakenteen tällaiseksi:

class Order {

  long id;
  List<OrderItem> orderItems;
}

class OrderItem {
  long id;
  Order order;
}

Ja tästä päästääkin syklisten referenssien helvettiin. Tämä on oliorakenteena ihan kelvollinen ja mahdollistaa juuri edellämainitun navigoinnin molempiin suuntiin (bidirectional one-to-many association). Tähän voisi iloisesti läpsäyttää JPA annotaatiot ja antaa sen valua kantaan ja kannasta triviaalilla koodilla.

Ongelmia tulee siinä vaiheessa kun haluttaisiin serialisoida tätä rakennetta johonkin hierarkiseen puurakenteeseen, esim. XML tai JSON. Ongelma johtuu siitä että dynaaminen sarjallistaja, esim. JAXB tai Jackson, käy läpi olion ominaisuudet yksi kerrallaan, ja kutsuu gettereitä, kerää tiedot, ja muuttaa ne tekstimuotoiseksi siirtokelpoiseksi dataksi. Siinä käy siis näin:

  1. Tallennetaan order, hienoa. Order on oliorakenne, joka sisältää orderItems listan, käydään se läpi
  2. Käsitellään jokainen orderItem vuorollaan. OrderItem on oliorakenne, joka sisältää viittauksen Order olioon
  3. Käsitellään jokainen viitattu Order vuorollaan. Order on oliorakenne joka sisältää OrderItems listan

Ja niin edelleen. Ikiliikkuja on keksitty. Tästähän saa palkakseen yleensä jonkun hienon kaatumisen ja cyclic/circlar reference errorin. Tai jos hauskasti käy, kone puuskuttaa hetken ja antaa stack overflow errorin tai out of memory errorin.

Mitä sitten on tehtävissä? Tämä artikkeli koskee JSON vaihtoehtoa, jos olet vielä XML parissa, olet pysyvästi helvetissä vailla poispääsyä, pahoittelen.

Jos käytät tätä esim. Jacksonin puitteissa, vaikkapa REST-rajapinnassa, ratkaisutapoja on muutama (tosiasiassa osa näistä sopii XML hommiinkin, jos edellisestä kohdasta tuli paha mieli):

  1. Katkaise syklinen referenssiketju merkkaamalla jommassakummassa päässä referenssi ei-serialisoitavaksi. Tapoja tähän on monia, Jackson taitaa tukea esim. Javan transient avainsanaa, @JsonIgnore annotaatiota, ja luokkatasolla voi myös listata ohitettavat kentät @JsonIgnoreProperties-annotaatiolla
  2. On myös mahdollista merkitä master-dependant suhde Jackson annotaatioilla @JsonManagedReference ja @JsonBackReference
  3. Tehdään aina value/transfer object johon normalisoidaan kulloinkin tarvittavat tiedot
  4. Myös voi merkitä identity-kentät Jackson annotaatioilla, @JsonIdentityInfo kertoo mikä kenttä on uniikki avain, jonka jälkeen serialisoinnissa voidaan viitata vain id arvoon, ei käydä läpi koko sisältöä.
  5. @JsonView annotaation käyttö näkymien muodostamiseen

Kahdessa ensimmäisessä kohdassa on yksi ongelma: Ne eivät salli talsimista edestakaisin, vaan vain yhteen suuntaan. Mutta ne ratkaisevat syklisen referenssipulman katkaisemalla rekursioketjun, eli sopivat moneen tilanteeseen. Kolmas kohta sisältää potentiaalisesti hurjan paljon virhealtista käsityötä ja myöhemmin ylläptoa ja en ole ollut koskaan kummankaan suuri fani. Neljäs kohta generoi kauheaa huttua serialisoinnista, ja en ole vielä löytänyt sille hyötykäyttöä. Neljäs kohta on näistä oma suosikkini. Se voisi olla vielä parempikin mutta sillä ainakin pääsee alkuun. Ja uusin Spring, Spring Boot, ja JAX-RS yhdistelmä tukee näitä ihanasti.

Homma toimi näin: Merkataan @JsonView annotaatiolla ne kentät, joita halutaan ehdollisesti serialisoida tai olla serialisoitamatta. Parametrina tulee tyypin nimi, joka on yleensä Java rajapinta. Esim. näin:

class View {

interface GimmeOrderRows {}

interface GimmeOrderHeader {}

}

Nyt voidaan muokata aiempia koodeja näin:

class Order {
  
  long id;
  
  @JsonView(View.GimmeOrderRows.class)
  List<OrderItem> orderItems;

}

class OrderItem {
  long id;

  @JsonView(View.GimmeOrderHeader.class)
  Order order;

}

Nyt pystyt hakemaan assosiaatiot on-demand periaatteisesti, eli voit navigoida kummasta päästä vaan. Jos et anna JsonView-annotaatiota, oletuksena saat kaiken. Heti jos annat yhdenkin @JsonView annotaation, saat kaikki kentät joihin se täsmää tai joita ei ole millään JsonView annotaatiolla varustettu. Eli jos meillä olisi tämän näköinen jax-rs palvelu…

@GET
@Path("order")
@JsonView(View.GimmeOrderRows.class)
public Order fetchOrderWithItems(long id) {
  return orderRepository.getOne(id);
}

… niin syklisen referenssin peikko pysyisi piilossa. Koska Jackson serialisoisi Orderin, ja sen sisältämät OrderItemit id-arvoineen, mutta ei seuraisi enää polkua niiden sisältämiin Order-instansseihin.

Vastaavasti nyt voisi huoletta hakea vaikkapa yhden OrderItem instanssin OrderHeadereineen ilman syklisiä referenssejä:

@GET
@Path("orderitem")
@JsonView(View.GimmeOrderHeader.class)
public OrderItem fetchOrderItemWithOrder(long id) {
  return orderItemRepository.getOne(id);
}

Samalla tekniikalla voi noutaa ehdollisesti esim. salasanatietoja, tai binäärisisältöä, tai muuten vain pitkiä kenttiä. Yhdessä kohtaa voi olla vain yksi JsonView-parametri, mutta koska niillä voi olla perintähierarkioita jotka tunnistetaan, rajoitus ei ole paha. On myös mahdollista säätää sellainen oletus, että mitään kenttää ei palauteta elleivät jsonviewt täsmää – ei myöskään niitä joista annotaatio puuttuu kokonaan.

Nyt kun vielä saisi Javaan luontevan suorastaan sisäänrakennetun JSON rajapinnan….

IoT ja Asimovin kolme lakia

Yksi asia mikä on mietityttänyt jo jonkin aikaa on Internetin otusten tietoturva, ja myös etiikka. Olen samanaikaisesti hieman innoissani tulevaisuudesta jossa asioiden älyllistyminen ja verkottuminen tuo kosolti mahdollisuuksia joita emme vielä edes täysin tajua tai osaa edes kuvitella. Toisaalta asioista tulee monimutkaisempia, ja alttiimpia väärinkäytölle. Robotiikan tehtävä on helpottaa ihmisten elämää arjessa, ja osa sitä tarkoittaa rutiinitehtävien siirtämistä koneille, tehtävien automatisointia, jossa ihminen painaa nappia ja kone tekee monimutkaisen prosessin.

P1000644

Me elämme jo robotiikan aikakaudella. Yksi elämää helpottava robotti on pyykinpesukone. Samaan voi laskea toki auton – sähköhevosen, ja onpa noita oikeita robottipölynimureitakin jo olemassa. Mikä ero on sitten nykypäivän robotiikalla ja tulevaisuuden IoT:lla? Kaksi asiaa, itsenäinen äly ja päättelykyky, ja langattomat verkot. Ensimmäisen myötä valta ja vastuu kasvaa, ja toinen voi potentiaalisesti avata reittejä vihamielisille tai ilkikurisille tahoille. On eri asia, jos mato tunkeutuu palvelinhuoneeseen ja louhii tietoja tietokannasta tai kaataa koneen ylikuormittamalla jotain sen osaa – ja taas eri asia kun pätkä ohjelmakoodia ottaa auton renkaiden ohjauksen haltuunsa – tai ruohonleikkurin – tai pelkästään kosolti voimaa omaavan hydraulisen käsivarren. Eikä edes tarvitse lähteä kehittyneisiin robotiikkaprojekteihin joita on parhailaan käynnissä pelastustarkoituksiin tai sotateollisuuden tarpeisiin – mainittakoon esim. DARPA ja Boston Dynamics projektit.

Tietoturvan osalta asia on oikeastaan helpompi. Me olemme oppineet tekemään palvelimista yltympäriinsä suojattuja  – etenkin jos niissä kulkee rahaa tai luottamuksellisia tietoja. Mikään suojaus ei ole sataprosenttinen – mutta IoT puolella on merkittävästi suojausta parannettavissa ihan soveltamalla jo opittuja periaatteita luottamuksellisuuden, autentikoinnin, autorisoinnin, ja ohjelmistopäivitysten suhteen. Hyvä muistaa myös DDoS-hyökkäykset ja niiden seuraukset suunnittelussa. Tuntuukin siltä, että kun elämme ensimmäisen sukupolven innovaatioaaltoa, nämä asiat ovat vain jotenkin unohtuneet, tai niitä ei kiireessä ehditä tehdä – ennen kuin jotain vakavampaa tapahtuu ja on pakko huomioida tietoturva. Jos selaa vuoden verran taaksepäin uutisia, löytää useitakin tarinoita laitteiden tietoturva-aukoista jotka ovat alkeellisia, helposti hyödynnettävissä, ja monesti jo hyödynnettu. Tämä alue on kuitenkin otettavissa haltuun sille joka haluaa – ja tulevaisuudessa ei ole varaa olla haluamatta.

Chuck Norris ei pelkää muistivuotoja. Muistivuodot pelkäävät Chuck Norrisia.

Mielenkiintoisempi kysymys on sitten laitteiden etiikka. Kun päätäntävaltaa siirretään ihmiseltä koneille, tulee moraalisiakin kysymyksiä vastattavaksi. Ja ennen niitä muutama harmaan alueen toiminnallinen kysymys. Ne tulevat heti mietintään kun laitetta ei käytä ihminen sormi aktiivisesti napilla. Jos auto pysäköi itsenäisesti, ja ohjelmointivirhe saa sen kolhimaan toista autoa, tai eläintä, tai ihmistä – kenen vastuulle se menee? Tämä on vielä helpomman pään kysymys. Vaikeammaksi menee kun täytyy tehdä valintoja. Itseohjautuva auto ajaa moottoritiellä satasta, ja havaitsee eläimen tiellä. Jarrutusmatka ei riitä hallittuun pysäytykseen. Säästetäänkö kaikkea elollista ja tehdään lukkojarrutus, otetaan riski että auto lähtee luisuun ja ihmishenki on vaarassa? Vai priorisoidaanko pienikin riski ihmishengelle yli kaiken muun? Tämäkin menee yhä mielenkiintoisemmaksi jos tielle astuukin yllättäen toinen ihminen. Auto on ilmiselvä esimerkki ja vaikuttaa aika tieteistarinalta – mutta parin vuoden päästä itsenäisesti pysäköivät autot ovat markkinalla, ja varmasti vuosikymmenen sisään itsenäisesti ajavat – niitä on jo muun liikenteen seassa useammassa maassa. Ollaan melko kaukana vielä keinoäly-humanoidi-roboteista, mutta itsenäistä päättelyä analogisen sensoritiedon pohjalta nopeissa tilanteissa tehdään jo nyt.

Hazelcast Lego-Pi cluster

Tämän blogautuksen otsikossa viittaankin useimpien tuntemiin Isaac Asimovin robotiikan kolmeen sääntöön, jotka tieteiskirjallisuudessa nähtiin aikanaan loogisiksi tavoiksi ratkaista näitä ongelmia: Itsenäinen päättelykyky sallitaan, mutta se rajoittuu aina kolmeen rikkomattomaan sääntöön jotka ohjelmoinissa otetaan huomioon, ja näillä pääsäännöilläkin on keskinäinen prioriteetti. Näitä sääntöjä on sittemmin lisäilty ja modifioitu, ja tokkopa näillä pärjätäänkään nykymaailmassa, mutta mielenkiintoista ajatusten ruokaa joka tapauksessa:

  • A robot may not injure a human being or, through inaction, allow a human being to come to harm.
  • A robot must obey the orders given it by human beings except where such orders would conflict with the First Law.
  • A robot must protect its own existence as long as such protection does not conflict with the First or Second Laws.

Hyvä kuitenkin muistaa että IoT ei ole vain autoja. Tulevaisuudessa tulee olemaan älykelloja, älyvaatteita, älyreppuja, äly-uuneja, älypesukoneita, älykyniä, älyvaloja, älyvedenlämmittimiä, älykameroita, älykoptereita, ja kaikkea muuta älytöntä ja ihanaa. Ja kun sanon tulevaisuudessa, tarkoitan nyt. Sitä mitä on kymmenen vuoden päästä en osaa rehellisesti edes kuvitella. Mutta äly ja verkot mahtuvat jo kaikkialle, ja ne tulevat olemaan kaikkialla mihin mielikuvitus voi ne viedä. Ja mitä tulee tietoturvaan tai älyn etiikkaan, voimme oppia nämä asiat kaikki kantapään kautta, tai yrittää kerrankin olla fiksuja jo alusta alkaen ja tehdä ne mahdollisimman oikein.

Tulevaisuutta ei voi estää – vaikka haluaisikin – sen voi vain kohdata.

Solitalla on myös meneillään Think Tank IoT-teemalla, joten jos aihe ja muut näkökulmat muutokseen kiinnostavat laajemminkin, tuosta voi navigoitua eteenpäin: http://www.solita.fi/ajankohtaista/solitan-viides-think-tank-ryhma-pureutuu-teollisen-internetin-murrokseen/

Edit: Hauskasti osuva artikkeli tähän liittyen: http://www.tekniikkatalous.fi/tekniikka/google-korjasi-robottiautojaan-osaavat-nyt-rikkoa-liikennesaantoja-6000962

Internet of Messed-Up Things

Kirjailin taannoin kokeiluista mitä tein GoPiGo botilla. Viikonloppuna tuli firman bileissä filosofoitua taas kerran kaikenlaisista älyhärveleistä, niistä jotka lentävät, ja niistä jotka huristavat maata myöden. Vaikka dronet ovat juuri nyt pop, perinteiset maata pitkin mönkivät botit ovat helpompia (ja halvempia) kokeiluun ja opiskeluun.

2015-07-05 17.27.22

Minä tekäisin aikanaan nodejs:llä karun komentoliittymän gopigo-softan ja raspberry pi-ympäristön päälle. Siinä on websocket serverikomponentti, joka ottaa yksinkertaisia käskyjä vastaan ja välittää ne servoille ja moottoreille – ja vastavuoroisesti antaa takaisin mittatietoa. Sitten siinä on ugly as hell-tyyppinen poc-käyttöliittymä, joka on koodattu html-sivun sisään. Nodejs serveri käynnistää molemmat, ja kun tähän ottaa yhteyttä esim. kännykällä, hupi voi alkaa. Tosiaan toistaiseksi botin suurin saavutus on kiusata perheen koiraa ja vierailevia kissoja.

2015-07-01 22.21.13

Ajattelin kuitenkin avata tämän vähäisen näperryksen jos joku on myös kiinnostunut vastaavista. Valitsin noden pohjaksi juuri nopean ja joustavan kokeilun vuoksi. index.js tiedosto käynnistää yhteyden gopigo kerrokseen, ja potkaisee käyntiin pienen websocket sekä web serverin porttiin 3000, ja lataa index.html sivun. Se on verkkainen mutta riittävän nopea, jos haluaa turboahtaa lisää voi käytellä pi v2 myllyjä. Itselläni sattui juuri olemaan ylimääräinen 1st gen Pi tähän kokeiluun, kakkosgeneraatiolla on ylväämpiä tehtäviä 😉

Screenshot 2015-09-11 19.27.32

Jeah, ux suunnittelua mahdollisesti voisi vielä viedä pidemmälle. Kokeile kännykässä niin huomaat sen 😉

No niin, pidemmittä puheitta: https://github.com/crystoll/gopigo

NodeJS 4.0 on ulkona

Vain nopea päivitys itseä kiinnostavasta asiasta tähän väliin: NodeJS 4.0 julkaistiin tänään. Jos joku ihmettelee rajua hyppyä versionumerosta 0.12.7 tähän uuteen – kyseessä on historiallinen mergetys jossa nodejs ja iojs yhtyvät, siksi raju pomppu.

Suositusta myös sen käytöstä: Ellet jo ennestään käytä nvm versiomanageria node versioiden ajoon, hanki se! OS X:ssä esim. brew install nvm tuottaa tuloksen – jahka säädät vielä vähän lisää ohjeiden mukaan. nvm:llä voit asennella iloisesti vanhaa ja uutta rinta rinnan ja vaihtaa nopeasti kokeillaksesi. Olisin yllättynyt jos 4.0 sujahtaisi ongelmitta vanhan tilalle joka paikkaan.

Linkkivinkkejä:

https://nodejs.org/en/

https://medium.com/node-js-javascript/4-0-is-the-new-1-0-386597a3436d

http://apmblog.dynatrace.com/2015/09/05/all-you-need-to-know-about-node-js-4-0/

https://github.com/creationix/nvm