RESTAssured ja Istuntokeksit

REST ja istunnot ovat aikalailla paradoksi. Mutta silti joskus löytää itsensä tilanteesta jossa tilattomat REST palvelut ovat tilallisen autentikoinnin takana, esim. JSESSIONID evästeellä seurattuna (Ja kyllä, Suomen Kielitoimisto suosittaa nykyisin Cookielle termiä eväste, aiemmin keksi, ja uskokaa tai älkää, alunperin Taikapipari). Jos vielä heikommin käy, edessä voi olla joku single-signon hirviö joka tokenien sijasta käyttää useampaa istunto-evästettä. Ja jotenkin pitäisi saada kirjautumisen jälkeen istunnot säilymään.

Jos kyseessä on vain yksinkertainen JSESSIONID case, alkaen RestAssured versiosta 2 sille on tuki valmiina, parillakin eri tavalla. Nopein tapa saada sessio-cookie lähetettyä edestakaisin on tähän tapaan:

RestAssured.filters(sessionFilter);

Tuo pitää huolen, että sessio-eväste kaapataan vastauksista, ja liitetään pyyntöihin. Toimii heittämällä jos nimi on JSESSIONID – kunhan vain pidät huolen siitä, että sessionFilter on kaikissa kutsuissa sama, esim. luomalla sen staattisena:

public static final SessionFilter sessionFilter = new SessionFilter();

Entä jos evästeen nimi ei ole JSESSIONID? No, sen voi helposti muuttaa RESTAssured konfiguraatiolla:

RestAssured.config = newConfig().sessionConfig(new SessionConfig().sessionIdName("KOTLINSESSIONID"));

Entä jos pitäisi puljata useampia tracking cookieita, ja niiden nimet muuttuvat välissä? No ei hätää, sen voi hoitaa omalla custom filterillä, tähän tapaan:

public class CookieResenderFilter implements com.jayway.restassured.filter.Filter {

  private Map<String, Object> cookiesMap = new HashMap<String, Object>();

  @Override
  public Response filter(FilterableRequestSpecification filterableRequestSpecification,
                         FilterableResponseSpecification filterableResponseSpecification,
                         FilterContext filterContext) {
    filterableRequestSpecification.cookies(cookiesMap);
    Response response = filterContext.next(
      filterableRequestSpecification,
      filterableResponseSpecification);
    Map<String, String> cookiesReceived = response.getCookies();
    if (cookiesMap.size() == 0 && cookiesReceived.size() > 0) {
      for (String key : cookiesReceived.keySet()) {
        cookiesMap.put(key, cookiesReceived.get(key));
      }
    }
    return response;
  }
}

Ja tuo rekisteröidään käyttöön kuten yllä, taas pitäen huolta siitä että kyseessä on aina sama instanssi, ei aina uusi – koska sen jäsenmuuttujia käytetään tiedon varastointiin testisession ajan.

Oikeastihan tilallisuus + REST rajapinnat ovat helvetin seitsemännen tason keksintö ja jo tätä artikkelia kirjoittaessa tuli likainen olo, mutta aina ei pääse valitsemaan työkalupakkiaan. Kun pääsee, käyttäkää BCRYPT Token headereita, ihmiset!

Mainokset

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

SOAPUI, autentikointi ja Groovy

Testasin taannoin SOAPUI:lla REST rajapintoja suorituskykytestimielessä. Kun rajapinnat olivat julkisia, homma on hyvin helppoa. Mutta kun rajapinta onkin autentikoinnin takana, edessä on perinteinen testityökalujen haaste: Miten kirjautua sisään siten että seuraavat kutsut menevät samaan sessioon?

No sehän riippuu autentikoinnin tavasta. SOAP UI:sta on kirjoiteltu artikkeleita joissa käydään läpi miten siihen voi tehdä mm. BASIC/DIGEST autentikoinnilla säädöt paikalleen, ja samoin on OAUTH-standardille palikoita. Mutta yleinen ratkaisu miten siirtää tavaraa pyynnöstä toiseen oli tässä yksinkertaisin. Tempun salaisuus on tehdä test caseen erillinen askel, jossa varastoidaan autentikoinnin tieto kontekstiin, josta sen voi myöhemmin pukata haluamaansa paikkaan, esim. http headeriin tms.

Eli sanotaan että askel 1 on määritetty olemaan REST kutsu nimellä ’login’, joka lähettää tunnusta ja salasanaa, ja vastaanottaa tokenin, esimerkissä nimellä ’identityToken’. Seuraava askel on avata SOAP UI:ssa test case, ja lisätä sinne uusi askel.(Add Step – Groovy Script).

Tänne kirjoitellaan koodi joka imaisee tokenin vastauksesta, esim. tähän tapaan:

// This script will grab authentication token and store it in testcase properties so it can be used later

xmlResponse = new XmlSlurper().parseText(context.expand( ’${login#ResponseAsXml}’ ))

assert xmlResponse.sessionId != null
assert xmlResponse.sessionId.toString().length() > 0
log.info(’Got ’ + xmlResponse.sessionId)
testRunner.testCase.setPropertyValue( ”authToken”, xmlResponse.sessionId.toString())

Eli pari huomiota tästä esimerkistä:

  • Tässä hyödynnetään SOAPUI:n ominaisuutta auto-konvertoida json xml muotoon – eli vastaus voi olla xml tai jsonia, kaikki käy
  • context.expand viittaa login-stepin sisältöön xml:ksi rutistettuna
  • Pseudo-xml kenttiin viitataan xmlResponse.xxx tapaan kuten yllä
  • log.info on tässä vain esimerkin vuoksi, sillä saa tulostusta
  • testcasen propertyyn lisätään authToken niminen arvo jossa on sessionid varastoituna

Tätä voi sitten vuorostaan käyttää missä haluaakin, esim. header kentässä tai asserteissa, tähän tapaan:

${#TestCase#authToken}

SOAPUI ja LOADUI ovat ihan metkoja kapistuksia joilla aika helposti pyöräyttää vähän integraatio/api/suorituskykytestejä.

Glassfish 4.1, JAX-RS, Jersey, ja Jackson JSON-serializer

Jep, päivitin vähän servereitä Glassfish 4.1 versioon, ja vastaan tuli mielenkiintoinen bugi: JAX-RS palvelut lakkasivat toimimasta ja antoivat sensijaan kaikenlaisia herjoja, niistä ehkä mielenkiintoisin:

Severe: Error occurred when processing a response created from an already mapped exception.
Warning: StandardWrapperValve[com.qpr.entice.common.ApplicationConfig]: Servlet.service() for servlet com.mycompany.jaadajaada.ApplicationConfig threw exception
java.lang.ClassNotFoundException: com.fasterxml.jackson.module.jaxb.JaxbAnnotationIntrospector not found by com.fasterxml.jackson.jaxrs.jackson-jaxrs-json-provider [129]

Glassfish käyttää oletuksena Moxya JSON serialisointiin, mutta itse käytän Jacksonia suorituskyvyn ja ominaisuuksien johdosta. Mielenkiintoista kyllä Glassfish toimitetaan osittaisin Jackson kirjastoin joten sen käyttöönotto on niinkin helppoa kuin aktivoida se config tiedostossa, tähän tapaan:

import javax.ws.rs.ApplicationPath;
import org.glassfish.jersey.jackson.JacksonFeature;
import org.glassfish.jersey.server.ResourceConfig;

@ApplicationPath("rest")
public class ApplicationConfig extends ResourceConfig {

  public ApplicationConfig() {
    packages(true,
       "com.mycompany.jaadajaada",
       "com.wordnik.swagger.jersey.listing");
    register(JacksonFeature.class);
  }
}

Eli rekisteröidään JacksonFeature – ja samantien käytössä on Jackson kirjastot. Aiemmassa Glassfish 4.0:ssa tämä pelasi hienosti – mutta 4.1 antaa ylläolevaa virheilmoitusta. Kokeilin myös jättää JacksonFeaturen pois ja testata pelkkää Moxya mutta sieltä tuli ihan omat herjansa. (Tuossa muuten näkyy myös Swagger joka automaattisesti dokumentoi REST APIt – ja käyttää Jackson kirjastoja)

Mielenkiintoista virheilmoituksessa on, että se viittaa puuttuvaan tiedostoon joka itselläkin oli kirjastopoluissa – mutta käy ilmi kaksi asiaa: se pitää olla Glassfishin alla, ja sen pitää olla oikea versio. Tiedosto joka uupuu on jackson-module-jaxb-annotations-2.3.2.jar, sen pitäisi olla glassfishin alla kansiossa modules, ja tosiaan versionumero 2.3.2, ei uusin. Tämän päälle kun putsaa osgi-cachen ja buuttaa serverin, saa taas REST JSON palvelut takaisin. 😉

Eli aika spesifi pulma. Aika noloa että Glassfishistä on hiljalleen turvonnut kohtuullisen monoliittinen mötikkä jossa on paljon kirjastoriippuvuuksia joita oma sovellus ei pysty helposti ylikirjoittamaan. JBOSS muistuu mieleen äärimmäisen modularisuutensa ansiosta, mutta muistelen kyllä sielläkin olleen aina uuden julkaisun kohdalla kirjastohaasteita. Mutta siellä on esim. web services implementaatio mukavan helposti vaihdettavissa.

Mutta, minä taidan digressoida 😉 Tässä joka tapauksessa virheilmoitus, ja korjaus, ja tuossa linkkiä keskusteluun samasta pulmasta:

https://java.net/jira/browse/GLASSFISH-21141

Teoriassa on mahdollista tehdä korjaus siistimminkin, autodeploy/bundles kansioon asennettavana paikkana joka ei muuta serverin toimintaa, mutta itse en saanut sitä vaihtoehtoa toimimaan.

JAX-RS ja Swagger: Helposti dokumentoity REST API

Hetken aikaa jo on tykyttänyt mietintämyssyn alla miten REST rajapintoja voisi helposti dokumentoida – WSDL kun sieltä uupuu (Ja WADL:ia ei kukaan halua). Kuitenkin pitäisi pystyä kertomaan ja rekisteröimään missä osoitteissa on palvelua, mitä ne tekevät, miten niitä kutsutaan, jne, muutenkin kuin testeillä ja esimerkeillä.

Törmäsin mukavaan jersey-yhteensopivaan laajennukseen nimeltä jersey-swagger. Sovitin tätä Glassfishin versioon, jossa pyörii Jersey 2, ja sainkin jotain aikaan. Tässä muistiinpanoja:

Käyttöönotto vaatii muutaman askelen: Ensiksi täytyy Mavenissä tai Gradlessa ottaa käyttöön swagger jersey versiolle kaksi. Samalla on tuikitärkeää määrittää pari exclusionia, tai serverille päätyy duplikaatteja hieman eri versioista ja tulee mukavia poikkeuksia logista. Tässä toimivaksi testattu dependency Glassfish neloselle:

 <dependency>
   <groupId>com.wordnik</groupId>
   <artifactId>swagger-jersey2-jaxrs_2.10</artifactId>
   <version>1.3.7</version>
   <exclusions>
     <exclusion>
       <groupId>org.glassfish.jersey.media</groupId>
       <artifactId>jersey-media-multipart</artifactId>
     </exclusion>
     <exclusion>
       <groupId>org.glassfish.jersey.containers</groupId>
       <artifactId>jersey-container-servlet-core</artifactId> 
     </exclusion>
   </exclusions>
 </dependency>

Hyvä tarkistaa tässä välissä että vanhat palvelut edelleen toimivat, jos niitä on. Seuraavaksi rekisteröidään Swagger servicet. Tämän voi tehdä xml:ssä – mutta itse käytän ResourceConfig-luokkaa ja annotaatioita:

@ApplicationPath("rest")
public class ApplicationConfig extends ResourceConfig {
public ApplicationConfig() {
packages("my.own.resources","com.wordnik.swagger.jersey.listing");
 }
}

Noin, sieltä aktivoituu tosiaan pari resurssiluokkaa, automaattisesti /rest/api-docs osoitteella. Seuraavaksi rekisteröidään Swagger servlet. Tämänkin voi tehdä annotaatioilla mutta kun sattui olemaan vielä web.xml (se on ainoa paikka jossa voi tehdä distributable-setin ha klusteriin) – tein sen siellä näin:

 <servlet>
 <servlet-name>JerseyJaxrsConfig</servlet-name>
 <servlet-class>com.wordnik.swagger.jersey.config.JerseyJaxrsConfig</servlet-class>
 <init-param>
 <param-name>api.version</param-name>
 <param-value>1.0.0</param-value>
 </init-param>
 <init-param>
 <param-name>swagger.api.basepath</param-name>
 <param-value>http://localhost:8080/rest/api-docs</param-value>
 </init-param>
 <load-on-startup>2</load-on-startup>
 </servlet>

Nyt alkaa jo melkein tapahtumaan. Seuraavaksi pitää varustaa ainakin yksi palvelu @Api-annotaatiolla, metodeille voi pistää @ApiOperation, ja parametreille ja paluuarvoille @ApiModel annotaatioita. Jotta saat jotain näkyviin tarvitset ainakin yhden @Api-annotaatiolla varustetun palvelun, jolla on ainakin yksi @ApiOperation annotaatiolla varustettu operaatio.

Tähän tapaan:

@Path("/pet")
@Api(value = "/pet", description = "Operations about pets")
@Produces({"application/json", "application/xml"})
public class PetResource {
  ...
@GET
@Path("/{petId}")
@ApiOperation(value = "Find pet by ID", notes = "More notes about this method", response = Pet.class)
@ApiResponses(value = {
  @ApiResponse(code = 400, message = "Invalid ID supplied"),
  @ApiResponse(code = 404, message = "Pet not found") 
})
public Response getPetById(
    @ApiParam(value = "ID of pet to fetch", required = true) @PathParam("petId") String petId)
    throws WebApplicationException {
@ApiModel(value = "A pet is a person's best friend")
@XmlRootElement(name = "Pet")
public class Pet {
  @XmlElement(name = "status")
  @ApiModelProperty(value = "Order Status", required=true, allowableValues = "placed,approved,delivered")
  public void setStatus(String status) {
    this.status = status;
  }
  public String getStatus() {
    return status;
  }

Sitten voitkin käynnistellä serverin, ja kokeilla mitä löytyy kontekstin alta osoitteella /api-docs, /api-docs/myservicename, jne – tässä tietysti myservicename on joku palvelu johon olet noita Swagger-annotaatioita ripotellut.

Tuolta löytyy myös esimerkkiä live-sisällöstä, niin kauan kuin se tietysti on pystyssä:

http://petstore.swagger.wordnik.com/api/api-docs

Näyttäisi tämmöiseltä:

{"apiVersion":"1.0.0","swaggerVersion":"1.2","apis":[{"path":"/pet","description":"Operations about pets"},{"path":"/user","description":"Operations about user"},{"path":"/store","description":"Operations about store"}],"authorizations":{"oauth2":{"type":"oauth2","scopes":[{"scope":"write:pets","description":"Modify pets in your account"},{"scope":"read:pets","description":"Read your pets"}],"grantTypes":{"implicit":{"loginEndpoint":{"url":"http://petstore.swagger.wordnik.com/oauth/dialog"},"tokenName":"access_token"},"authorization_code":{"tokenRequestEndpoint":{"url":"http://petstore.swagger.wordnik.com/oauth/requestToken","clientIdName":"client_id","clientSecretName":"client_secret"},"tokenEndpoint":{"url":"http://petstore.swagger.wordnik.com/oauth/token","tokenName":"auth_code"}}}}},"info":{"title":"Swagger Sample App","description":"This is a sample server Petstore server.  You can find out more about Swagger \n    at <a href=\"http://swagger.wordnik.com\">http://swagger.wordnik.com</a> or on irc.freenode.net, #swagger.  For this sample,\n    you can use the api key \"special-key\" to test the authorization filters","termsOfServiceUrl":"http://helloreverb.com/terms/","contact":"apiteam@wordnik.com","license":"Apache 2.0","licenseUrl":"http://www.apache.org/licenses/LICENSE-2.0.html"}}

Törmäsin myös toiseen aika metkaan standardiin – ALPS. Sitä näyttäisi osaavan Spring Framework hyvin, mutta ainakan nyky-jerseystä ei löytynyt helppoa ratkaisua siihen. Pidän tätäkin kuitenkin silmällä. Looking good!

http://www.dzone.com/links/r/spring_data_rest_now_comes_with_alps_metadata.html

http://alps.io/

Näistä linkeistä löytyy vähän lisää ideoita. Varovaisuutta vanhemman Swagger-version kanssa!

https://github.com/wordnik/swagger-core/wiki/Java-JAXRS-Quickstart

 

 

Spring 4 + Spring Boot + Jax-rs + Jersey + Jackson + JsonObject toimimaan

Sain hiukan takkua yrittäessäni konvertoida Java EE projektia toimimaan Springin päällä standardin Jax-RS rajapinnan avulla sensijaan että olisin käyttänyt Springin omia.

Tässä toimivaksi testattu dependencies-osa pom.xml tiedostosta, jossa riippuvuudet paikallaan. Pohjalla on Spring boot, joka on jo otettu parent-osion avulla mukaan (kuten aiemmassa blogissani näytin):

Huomaa että riippuvuudet joissa ei ole versionumeroa tulevat Spring boot perinnässä dependencyManagement osan kautta, esim. log4j, mysql, jne. Tässä on myös muutama extra riippuvuus JAX-RS:n ohella sekä mysql ajurit.

jersey.version property on asetettu arvoon 2.8 ylempänä properties-osiossa, jota tässä ei ole näytetty.

<dependencies>

  <!-- Let's get this started with Spring Boot for web apps -->
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
  </dependency>
 
  <!-- Do not include tomcat server libs, as we package .war -->
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-tomcat</artifactId>
    <scope>provided</scope>
  </dependency> 
 
  <!-- Include Spring Data, jpa, and derby for database testing -->
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
  </dependency>


  <!-- Log4j support so we can get some log output -->
  <dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
  </dependency> 

  <!-- Mysql database -->
  <dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
  </dependency>

  <!-- Jersey JAX-RS 2.0 support -->
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-jersey</artifactId>
    <version>1.0.2.BUILD-SNAPSHOT</version>
  </dependency> 

  <dependency>
    <groupId>org.glassfish</groupId>
    <artifactId>javax.json</artifactId>
    <version>1.0.4</version>
    <scope>runtime</scope>
  </dependency>

  <dependency>
    <groupId>javax.json</groupId>
    <artifactId>javax.json-api</artifactId>
    <version>1.0</version>
  </dependency>

  <dependency>
    <groupId>org.glassfish.jersey.core</groupId>
    <artifactId>jersey-server</artifactId>
    <version>${jersey.version}</version>
  </dependency> 

  <dependency>
    <groupId>org.glassfish.jersey.core</groupId>
    <artifactId>jersey-common</artifactId>
    <version>${jersey.version}</version>
  </dependency> 

  <dependency>
    <groupId>org.glassfish.jersey.core</groupId>
    <artifactId>jersey-client</artifactId>
    <version>${jersey.version}</version>
  </dependency> 
 
 <dependency>
    <groupId>org.glassfish.jersey.media</groupId>
    <artifactId>jersey-media-json-processing</artifactId>
    <version>${jersey.version}</version>
  </dependency> 
 
  <dependency>
    <groupId>org.glassfish.jersey.containers</groupId>
    <artifactId>jersey-container-servlet</artifactId>
    <version>${jersey.version}</version>
  </dependency> 

  <dependency>
    <groupId>org.glassfish.jersey.ext</groupId>
    <artifactId>jersey-spring3</artifactId>
    <version>${jersey.version}</version>
  </dependency> 

  <!-- JAXB-to-JSON Serialization support -->
  <dependency>
    <groupId>org.glassfish.jersey.media</groupId>
    <artifactId>jersey-media-json-jackson</artifactId>
    <version>${jersey.version}</version>
  </dependency> 
 
  <dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.4.0-rc3</version>
  </dependency> 

  <dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-core</artifactId>
    <version>2.4.0-rc3</version>
  </dependency> 
 
  <!-- Using restassured for API testing -->
  <dependency>
    <groupId>com.jayway.restassured</groupId>
    <artifactId>rest-assured</artifactId>
    <version>2.3.1</version>
    <scope>test</scope>
  </dependency>
 </dependencies>

Ylläolevassa on tuotu myös jersey-kirjastoja uudestaan vaikka osa niistä tuleekin spring-jersey ja spring-boot-jersey mukana. Mutta näin saadaan tuoreimmat versiot käyttöön ja versio helposti säädetyksi kerralla. Tässä voisi myös käytellä dependencyManagement-osiota versioiden hallintaan.

 

 

 

Java 8 ja Glassfish 4 Raspberry Pihin

No niin, olen taas ollut Cyberhome-projektin parissa, eli tietotekniikkaa omaan kotiin. Projekti etenee laiskasti kun vapaa-ajalla ei jaksa/pysty/huvita ihan kaikkia iltoja koodailla ja säätää. Mutta eilen tuli vähän läpimurtoa taas Raspberry Pi (raspi) minitietokoneen osalta – siitä ja sen serkuksista on tarkoitus tulla autonomisen mutta tarvittaessa mobiilisti ohjattavan kodinohjausjärjestelmän aivot.

Raspberry Pi ohjaa kotia

Raspille on paljon artikkeleita pythonille, mutta päätin tapella siihen sensijaan Java-puolen kuntoon. Jotta saadaan vaikeustaso kohdalleen, uusimman release candidate version Java 8 versiosta. Siinä ei ollut sinällään mitään ihmettä, pudottelin Hard Float version JDK 1.8:sta arm prosesorille, purin sen /opt alle, fiksasin linkit ja ympäristömuuttujat, ja nyt pyörii Java 8 – ja lambdat ja streamit – täysimuotoisena.

Seuraava etappi oli Glassfish 4. Siihen oli yllättävän paljon ohjeistusta, mutta pääosin vain tiputin sen paikalleen /opt alle, ja säädin vähän muistia ja timeout arvoja, tähän tapaan:

<jvm-options>-XX:MaxPermSize=96m</jvm-options> 
 <jvm-options>-XX:PermSize=32m</jvm-options> 
 <jvm-options>-Xmx128m</jvm-options>
<thread-pool name="admin-thread-pool" max-thread-pool-size="50" max-queue-size="256" idle-thread-timeout-seconds="3600"></thread-pool> 
 <thread-pool name="http-thread-pool" idle-thread-timeout-seconds="3600"></thread-pool> 
 <thread-pool name="thread-pool-1" max-thread-pool-size="200" idle-thread-timeout-seconds="3600"></thread-pool>

Jep, se ei ole nopea, mutta se toimii. Full Java EE 7 Stack, plus uudet Java 8 piirteet.

Tämän päälle rojautin sinne MySql ajurit ja kannan. Ja asensin sen automaattikäynnistyväksi ajamalla Glassfish työkalun asadmin create-service. Tässä tulikin pikku pulma. Glassfish käynnistyi kyllä ajamalla sudo /etc/init.d/GlassFish_domain1 – kuten pitääkin. Mutta ei toiminut buutatessa automaattisesti.

Pikkasen yömyöhän kokeilemisen avulla löytyi ongelma yllättävästä päästä – tuossa Glassfish scriptissä tulisi olla oikeanlaiset headerit, ilman niitä se ei asennu palveluksi. Lisäsin sinne seuraavaa ja se alkoi toimimaan:

### BEGIN INIT INFO
# Provides: Glassfish
# Required-Start: $local_fs $network
# Required-Stop: $local_fs
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
### END INIT INFO

Sitten tuunasin vielä Netbeansin lähettämään työn alla olevat softat suoraan sftp:llä Glassfishin autodeploy-kansioon. Tästä oli hyvät ohjeet ant:lle, mutta halusin tehdä saman Mavenillä – ja pikku säätelyn jälkeen sekin onnistui. Se osuus taas näyttää tältä:

<build>
 <plugins>
<plugin>
 <artifactId>maven-antrun-plugin</artifactId>
 <executions>
 <execution>
 <phase>install</phase>
 <configuration>
 <tasks>
<echo level="info" message="Copying dist directory to remote Pi"/>
<exec executable="d:/utils/pscp.exe" dir="${basedir}">
<arg value="target/${project.build.finalName}.${project.packaging}"/>
<arg value="pi@raspissh:/opt/glassfish4/glassfish/domains/domain1/autodeploy"/>
</exec>
</tasks>
</configuration>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>

Ja nyt on homma aika saumatonta. Tuo ylläoleva toki vaatii lisäksi oikeat työkalut, omassa tapauksessani putty-kirjaston ohjelmistoja, ja tuo raspissh avain pitää luoda ja asentaa paikalleen raspiin jotta salasanoja ei tarvitse kirjoitella vaan autentikointi hoituu lennossa avaimilla.

Päräytin varhaiskokeiluna Java EE 7 mukaisen REST palvelun joka käyttää Pi4J kirjastoa ja kääntää releitä ja kytkimiä ja ledejä päälle ja pois. Siihen päälle HTML5+JQuery käyttöliittymä joka niitä JSON komentoja heittelee sisään ja ulos. Alkaa näyttämään hyvältä. Kiitos Mavenin voin pajautella muutoksia vaikka tekstieditorilla ja asentaa uuden softan komentoriviltä.

Jahka saan vielä paketin vahvavirta-releitä ja vähän konsultointiapua, kodin automaatio etenee taas pykälällä. Tarkoitus on mm. ajastaa lämmitystoimintoja älykkäämmin säilyttäen silti override-mahdollisuudet mm. kännykällä ja ihan vanhanaikaisella Big Red Button(tm):lla.

Tässä pari vinkkiä mistä oli itselle säätäessä apua (mikään ei tietysti toiminut heti heittämällä kuin Strömsössä):

http://www.thehecklers.org/2013/10/27/glassfish4-raspberrypi/

http://blog.c2b2.co.uk/2013/06/getting-started-with-glassfish-4.html

https://blogs.oracle.com/speakjava/entry/integrating_netbeans_for_raspberry_pi