Angular2 + NPM + Webpack

Lupailin kirjailla vähän lisää fiksusta Angular2 setupista tuotantohommiin. Tarkoitus oli tunkkailla minimaalinen paketti jossa on helppo erottaa eri tekniikoiden tehtävät. Valitettavasti Angular2 on aika liikkuva kohde tällä hetkellä – ja kiirettäkin on pitänyt, joten yksinkertainen POC projekti on jäänyt haaveeksi.

Onneksi joku muu on tehnyt sellaisen. Tällä hetkellä fiksuinta on aloittaa kokeilut ylläpidetystä Angular2 Webpack Starter projektista: https://github.com/AngularClass/angular2-webpack-starter

Valitettavasti tuo projekti ei ole yksinkertainen eikä siisti, mutta hoitaa homman. Ja se on kuitenkin hyvin linjassa sen kanssa mitä itse ajattelen. Angular2 kehittyy vauhdilla juuri nyt, ja joka releasen myötä se on siistimpi ja eheämpi ja vähemmän tarvitaan purkkaa taustalle. Sitä myöten voisin kuvitella että tämä webpack starterkin yksinkertaistuu.

Joka tapauksessa, vähän saatesanoja projektin suhteen. Senhän saa käyttöön nätisti kokeiluun (kun nodet on asennettuna), esim. näin:

 

git clone https://github.com/angularclass/angular2-webpack-starter.git
cd angular2-webpack-starter
npm install
npm start
curl http://localhost:3000

Curlin sijasta voit luonnollisesti tyypittää oikealla selaimella. Kuten kynäilin viimeksi, Bower on vähän menemässä muodista pois, samoin Grunt – paljon kuin sitä ja Gulpia rakastankin. Jos haluaa, voihan molempia noita käytellä vieläkin, ja voi olla joku tehtävä jossa Gulp voi olla esim. hyödyllinen. Mutta vanilla on päivän maku näissä asioissa, ja aikalailla paljon saa aikaan ihan vain puhtaalla JavaScriptillä ja NPM:llä.

Kannattaa luoda silmäys package.json tiedostoon. Se on tässä kaiken avain. Ja voi olla pelottava kokemus tässä vaiheessa jos on tottunut että se on vain kirjastomanifesti. Tosiaan devdependencies-lista on aika kattava näinkin yksinkertaiselle projektille, mutta tulee yksinkertaistumaan jatkossa. Ja toki siinä on jo aika kattavasti testaustyökaluja mukana. Pääjuju kuitenkin, että riippuvuudet on fiksuinta hakea kaikki npm keinoin, softan omat riippuvuudet ovat dependencies kohdassa, ja Boweria ei käytetä.

Mielenkiintoinen osio on kuitenkin scripts-osio. Vielä pari vuotta sitten oli tavanomaista että tämä oli tyhjä tai siinä oli 1-2 kohtaa, build ja test. Nyt tässä näkyy hyvin painopisteen muutos. Perinteiset grunt/gulp työvaiheet on nyt npm scripts työvaiheina, ja ne ovat modulaarisia ja ketjutettuja. Tarvittaessa käytetään noita dpm dev moduuleja, esim. rimraf poistaa tyylikkäästi tarvittavat hakemistopuut. Jos tuosta poimii muutaman mehukkaan ja keskeisen komennon niin ne ovat:

"clean": "npm cache clean && rimraf node_modules doc typings coverage dist"
"build:dev": "webpack --progress --profile --colors --display-error-details --display-cached"
"server:dev": "webpack-dev-server --progress --profile --colors --display-error-details --display-cached --content-base src/"

Näitä pääsee ajamaan komennolla npm run, esim.

npm run build:dev (ajaa kehitysversion buildin)

Samaan listaan on myös määritetty oikopolkuja yleisimpiin, esim:

"build": "npm run build:dev"
"server": "npm run server:dev"

Siinäpä nuo yleisimmät. Ja loput hoitaa webpack. Webpackillä on tässä kaksi keskeistä tehtävää: Tärkein on lähdekoodin prosessoiminen halutuksi paketiksi (sisältäen tarpeen mukaan babelifioinnit, minifioinnit, obfuskoinnit, jne), ja sen ohella myös sisäänrakennettu testiserveri on mukava. Konfiguraatiot käännöksiin löytyvät projektin juuresta, esim. tsconfig.json ja webpack.config.js. En pui niiden sisältöä tässä sen enempiä, mutta ne ovat aika selkeitä ja tehtäväänsä keskittyneitä. Ehkä hyvä mainita että toisin kuin Angularin omassa yksinkertaisessa tutoriaalissa, moduulijärjestelmänä SystemJS on korvattu CommonJS:llä.

Mitäpä muuta? Sain aiemman artikkelin suhteen vähän kommenttia/kysymystä tuosta Mavenin käytöstä. Itsehän tyypillisesti teen backendit Javalla/Spring Bootilla, ja frontend on vain osa kokonaisprojektia. Ja koska asiat on kiva pitää yksinkertaisena, samalla kun buildataan Mavenillä backend hyväksi, on mukava rusauttaa läpi myös frontend buildit. Käytän tähän tosiaan eirslett maven plugaria, joka voi yksinkertaisimmillaan näyttää tältä:

<plugin>
  <groupId>com.github.eirslett</groupId>
  <artifactId>frontend-maven-plugin</artifactId>
  <version>0.0.27</version>
  <executions>
    <execution>
      <id>install node and npm</id>
      <goals>
        <goal>install-node-and-npm</goal>
      </goals>
      <configuration>
        <nodeVersion>v5.5.0</nodeVersion>
        <npmVersion>3.3.12</npmVersion>
      </configuration>
    </execution>
    <execution>
      <id>npm install</id>
      <goals>
        <goal>npm</goal>
      </goals>
    </execution>
    <execution>
      <id>npm build</id>
      <goals>
        <goal>npm</goal>
      </goals>
      <configuration>
        <arguments>run build:prod</arguments>
      </configuration>
      <phase>compile</phase>
    </execution>
  </executions>
</plugin>

Käytännössä siis tuo tarkoittaa:

  • Varmista että node ja npm ovat asennettuna paikallisesti
  • Aja npm install
  • Aja npm run build:prod scripti – automaattisesti Mavenin Compile vaiheessa, eli samalla kuin backend kooditkin käännetään

Yksinkertaista. Webpack on konfiguroitu puskemaan prosessoidut .js tiedostot, html:t ja muut staattiset osat sopivaan kansioon, josta Mavenin paketointivaihe ne sitten nappaa mukaan .war pakettiin.

Huomaa että jos haluat ajaa Mavenin läpi muitakin npm taskeja, se onnistuu määrittämällä sopiva Maven property jonka voit komentoriviltä ylikirjoittaa, ja käyttämällä sitä tuon npm taskin konfiguraatiossa, esim. näin:

<execution>
  <id>npm build</id>
  <goals>
    <goal>npm</goal>
  </goals>
  <configuration>
    <arguments>${npm.argument}</arguments>
  </configuration>
  <phase>compile</phase>
</execution>

En kuitenkaan nykyisellään koe tälle erityistä tarvetta. Mavenin päätehtävä on varmistaa että riippumatta koneen varustuksesta paketti osaa buildata itsensä eheäksi itsenäisesti, oli sitten kyseessä Jenkins ympäristö tai uuden projektiin tulevan kehittäjän kone, tai vanhan devaajan uusi kone. Eli toiminta varmistetaan kaikissa olosuhteissa. Mutta noita muita npm käskyjä voi hyvin ajaa ohi mavenin esim. frontend-painotteisten kehittäjien toimesta, esim.watch on varsin hyödyllinen kun haluaa saada käyttöliittymäkehityksessä jatkuvaa palautetta. Siksi perusbuildi Mavenin kautta ajettuna usein riittää, ja oletuksena production buildi on tietysti hyvä.

Tässä mietteitä tällä kertaa. Matka vilkkaassa frontend maailmassa jatkuu….

 

 

Mainokset

Netbeans 8.0.1 ja JavaScript, Angular, Require, Grunt,…

Netbeans 8 on ollut kaikista kehutuista ominaisuuksistaan huolimatta Javascript käytössä lähinnä glorifioitu tekstieditori. Tarkoittaen, että ominaisuudet vaativat toimiakseen tietyn HTML5 tyyppisen projektin, esim. Maven tai web projektit eivät kelpaa, saati sitten freeform kansiot. Sen kanssa on pärjäilty tähän asti, lähinnä Maven-tuen ja Git-tuen hyvän integraation vuoksi.

Nyt tuli kuitenkin putkesta ulos 8.0.1 päivitys, ja siinä on pieniä parannuksia tälle kentälle. Jatkossakin on vielä varaa paljon parantaa – mutta jokainen askel eteenpäin on hyvästä.

Yksi päivityksistä on parempi tuki require-moduulihallinnalle. Se löytyy nyt myös Maven projektin asetuksista:

RequireJS kirjastot

Kuten dialogista näkee, voi halutessaan määritellä polkuja myös manuaalisesti jotta moduulit löytyvät – rasti ruudussa pitäisi kuitenkin pitkälti riittää. Kun moduulit löytyvät, monet intellisense/content assist mahdollisuudet paranevat:

Content assit for require paths

Intellisense

Tosiaan kaukana ollaan vielä täydellisyydestä, parannettavaa on vielä. Intellisense on paranemaan päin mutta esim. angular templateiden ymmärtäminen aivan alkutekijöissä: nykyisellään niistä tulee lukemattomia html5 sääntömotkotuksia mm. attribuuteista ilman arvoja tai siitä mikä elementti voi olla missäkin kohtaa. Useimmat niistä ovat vääriä hälyytyksiä, mutta niiden sekaan häviävät sitten merkityksellisetkin.

Tosiaan editoripuolella suosittu vaihtoehtohan on Webstorm, itse en siitä niin innostunut koska kaupallinen ja koska Maven projektituki edelleen ainakin omalle logiikalle kehno. Muita hyviä keveämpiä open source editoreita ovat mm. Brackets, sekä Sublime. Mukavasti grunt/mavenillä rakennetussa projektissa on kivaa se että voi käyttää mitä editoria haluaa, kunhan koodin muotoiluasetukset ovat yhtenevät. Brackets tuli blogin lukijavinkkinä, ja olen siihen tykästynyt.

Maven, Glassfish 4 ja Cargo plugin

Kun hioo jatkuvaa integraatiota kuntoon tulee ennen pitkää ajankohtaiseksi pohdiskella miten automatisoidaan Maven buildien asennus etäservereille – kehittäjätestiservereille, hyväksyntätestiservereille, ja tuotantoonkin. Tapojahan tähän on monia – serverien omat työkalut, ihan tiedostotasolla toiminta ja hot deploy. Itse kuitenkin halusin kytkeä tämänkin Maveniin, se helpottaa kovasti mm. Jenkinsin kanssa toimintaa ja pitää munat samassa korissa.

Kokeilin muutamaa plug-inia, ja suosikiksi niistä erääntyi Cargo plug-in. Muissa tuli vastaan bugeja, ja Cargon konfiguraatio oli simppeleimmästä päästä joten se päätyi mukaan. Mukavaa on myös se, että Cargo ei ole yhden serverin ihme vaan tukee valtaisaa määrää Java Appservereitä.

Eli lisätään pom.xml tiedostoon tällainen plug-in:

 <!-- Maven Cargo plug-in to deploy .war to test servers -->
  <plugin>
    <groupId>org.codehaus.cargo</groupId>
    <artifactId>cargo-maven2-plugin</artifactId>
    <version>1.4.8</version>
    <configuration>
      <container>
        <containerId>glassfish4x</containerId>
        <type>remote</type>
      </container>
    <configuration>
      <type>runtime</type>
      <properties>
        <cargo.hostname>munserveri</cargo.hostname>
        <cargo.remote.username>muntunnus</cargo.remote.username>
        <cargo.remote.password>munsalasana</cargo.remote.password>
      </properties>
   </configuration> 
   </configuration>
    <dependencies>
      <dependency>
        <groupId>org.glassfish.deployment</groupId>
        <artifactId>deployment-client</artifactId>
        <version>3.2-b06</version>
      </dependency>
    </dependencies>
  </plugin>

Ja siinä se. Tuossa yllä munserveri on tietysti oman Glassfish 4 serverin osoite, muntunnus ja munsalasana sen admin-oikeudet omaavat tunnukset.

Nyt voit ajaa seuraavia komentoja:

mvn clean package cargo:deploy
mvn clean package cargo:redeploy
mvn cargo:undeploy

Yritin viritellä tätä toimimaan force-parametrilla siten että deploy hoitaisi myös redeploy-toiminnon – mutta jostain syystä ei pelittänyt ainakaan tässä ympäristössä. Ratkaisuna on ajaa aina cargo:redeploy, se ei näytä pahastuvan ellei serveriltä softaa ennestään löydykään, mutta deploy pahastuu jos softa on jo serverillä samalla nimellä, ja undeploy pahastuu jos softaa ei ole.

Seuraava askel on sitten tuunata profiileja käyttöön. Niillä voit säätää helposti serveriosoitteet, salasanat ja tunnukset vaikka miten monelle eri serverille ja käytellä niitä esim. näin:

mvn -Pintegration-test package cargo:undeploy cargo:deploy

Varmaan on fiksumpiakin tapoja tehdä näitä, esim. kenties force parametrin saa toimimaan, kenties redeploy hoitaa saman kuin undeploy+deploy. Tällä pääsee kuitenkin alkuun, ja kommentoida sopii jos keksii liukkaampia tapoja.

Plug-in on CodeHousin käsialaa ja löytyy täältä: http://cargo.codehaus.org/

 

 

Ja näin opimme taas jotain kivaa ja hyödyllistä 😉

 

 

 

Koodin laadun hifistelyä – Maven, AntRun, SonarQube

Hetken SonarQubea käytettyäni olen suuri fani. Kun koodaustiimi saa jatkuvasti metriikkaa tekemisistään, on helpompi tehdä asiat alusta asti oikein ja välttää teknistä velkaa. Olen jo aiemmin ollut ihastunut staattisiin lähdekoodianalyysiohjelmistohin, mutta Sonar on – puutteistaankin huolimatta – paljon laajempi ja osittain helppokäyttöisempi paketti.

Tässä pari uutta havaintoa matkan varrelta:

Sonarista saa näppärästi web service rajapinnan kautta metriikkaa ulos siitä miten homma etenee – näistä voi vuorostaan jopa päräytellä näppäriä graafeja tai radiaattoreita.

Esim: http://munsonarserveri:9000/api/timemachine?resource=1&metrics=coverage

Pajauttaa aikakoneesta koodin kattavuusmetriikkaa tämännäköiseen json muotoon:

[{"cols":[{"metric":"coverage"}],"cells":[{"d":"2014-06-04T15:42:12+0300","v":[0.0]},{"d":"2014-06-05T09:21:24+0300","v":[11.9]},{"d":"2014-06-09T10:22:23+0300","v":[12.8]},{"d":"2014-06-10T08:03:49+0300","v":[12.8]},{"d":"2014-06-11T07:36:49+0300","v":[14.1]},{"d":"2014-06-11T13:59:50+0300","v":[18.3]}]}]

Tuossapa tietysti sampleja joissa aikaleima, ja testikattavuuden prosentuaalinen määrä – toivon mukaan nouseva trendi.

Samoin irtoaa esim. tech debt mittari, LOC mittari, jne, joilla pystyy seuraamaan mitä tapahtuu. Nämä vaikka Android kännykän näytölle pyörimään tai robotti motkottamaan jos ylityksiä tapahtuu.

Toinen huomio: Javascript projektissa ei alkuun code coverage mittailut toimineet, vaikka lcov kattavuustiedostoja generoimmekin. Keksin pari heikkoutta Maven projektihässäkässä Sonarin suhteen:

– Sonarista on jokin aika sitten poistettu käytöstä parametrit joilla voi kertoa mistä junit testiraportit löytyvät. Monet tutorialit viittaavat vielä niihin, mutta ne eivät siis toimi.

– Grunt rakentelee kyllä iloisesti lcov-standardin mukaisia raportteja Karma -testeistä. Mutta ne menevät ikävästi kansioon jonka nimessä on selain ja käyttöjärjestelmä – jotka voivat vaihdella, ja joissa on välilyöntejä. Toinen ongelma oli lcov tiedoston sisältö – siellä oli suhteellisia polkuja jotka eivät olleet suhteessa Maven projektiin, vaan testikonfiguraatioiden paikkaan.

Joten muutama korjaus joita tein:

Ensinksi AntRun plug-in kopsaamaan lcov tiedosto fiksumpaan ja vakioidumpaan paikkaan – ja regexp replace fiksaamaan nuo polut:

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-antrun-plugin</artifactId>
  <version>1.7</version>
  <executions>
    <execution>
      <id>copy-lcov-file-to-target</id>
      <phase>test</phase>
      <goals>
        <goal>run</goal>
      </goals>
      <configuration>
        <skip>${skipTests}</skip>
       <target>
         <copy todir="target" flatten="true">
           <fileset dir="target">
             <include name="surefire/PhantomJS*/lcov.info" />
           </fileset>
         </copy>
         <replaceregexp byline="true"
 file="${basedir}/target/lcov.info"
 match="SF:./app/"
 replace="SF:src/main/frontend/app/" />
       </target>
      </configuration>
    </execution>
  </executions>
</plugin>

Sitten vain sonaria varten yksi property asetus Maven pom.xml tiedostoon:

<properties>
    <sonar.javascript.lcov.reportPath>target/lcov.info</sonar.javascript.lcov.reportPath>
</properties>

Ja näin alkaa tippumaan testikattavuudesta raportteja.

Protractor alkoi myös tiputtelemaan JUnit yhteensopivia raportteja kun lisäili sen konfiguraatiotiedostoon tällaista:

 onPrepare: function() {
   require('jasmine-reporters');
   jasmine.getEnv().addReporter(
     new jasmine.JUnitXmlReporter('../../../target/surefire', true, true,'e2e-TEST-'));
 },

Siinäpä nämä tällä kertaa. Sonarin aikakone – time machine – antaa mukavaa metriikkaa siitä miten tarkkailija muuntaa tarkkailtavaa – quality is in the eye of the beholder.

timemachine

 

 

 

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.

 

 

 

Maven Elastic Beanstalk Plugin

Hiljattain kirjoittelin Spring Boot projektista, jolla on helppo saada aikaan web sovellus joka tukee Java EE teknologioita mutta pyörii jopa Tomcat serverillä. Yksi hyöty mikä tästä saavutetaan on helppo mahdollisuus tuikata paketti vaikka Elastic Beanstalk-pilveen.

Tässä on Maven plug-in joka hoitaa homman (lisää build-osioon pom.xml:ssä):

<plugin>
 <groupId>br.com.ingenieux</groupId>
 <artifactId>beanstalk-maven-plugin</artifactId>
 <version>1.3.1</version>
 <configuration>
 <s3Bucket>elasticbeanstalk-eu-west-1-11111111111</s3Bucket>
 <s3Key>${project.artifactId}/${project.build.finalName}-${maven.build.timestamp}.war</s3Key>
 <region>eu-west-1</region>
 <!-- will save as s3://[bucket name]/[artifactId]/artifactId-version-TIMESTAMP.war -->
 <autoCreateApplication>true</autoCreateApplication>
 <applicationName>${project.artifactId}App</applicationName>
 <environmentName>${project.artifactId}Env</environmentName>
 <cnamePrefix>${project.artifactId}app</cnamePrefix>
 <versionLabel>${project.version}-${buildNumber}</versionLabel>
 </configuration>
 </plugin>

Luonnollisesti s3 bucket id:t ja regionit kuntoon tuossa. Ja näin aukeaa uusia komentoja:

mvn beanstalk:upload-source-bundle beanstalk:create-application-version beanstalk:create-environment

Ylläoleva luo beanstalkin avulla Amazon AWS klusteriin uuden web serverin pyörittämään softaa.

Tämä taas päivittää softan uuteen versioon, mukava yhdistää esim. mvn clean install käskyihin:

mvn beanstalk:upload-source-bundle beanstalk:create-application-version beanstalk:update-environment

Huom! Nämä kaverit kaipaavat myös autentikointia, se taas onnistuu kun tekee settings.xml tiedostoon .m2 kansioon tällaista:

<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0
 http://maven.apache.org/xsd/settings-1.0.0.xsd">
   <servers>
     <server>
     <id>aws.amazon.com</id>
       <username>AATAJABEETÄJACEETÄ</username>
       <password>HÄRPÄTIHÄRPÄTI</password>
     </server>
   </servers>
 </settings>

Nämä tiedot tietysti löytyvät Amazon AWS alta, ja luonnollisesti salasanasta on parasta käyttää kryptattua muotoa, mitä Maven tukeekin mukavasti. Huom! Tietysti myös ylläolevassa plugin-asetuksessa syytä asettää tuo s3 bucket id oikein.

Lisäksi, jos lisäät tämän plugin-osion configurationin jälkeen, deploy tapahtuu automaattisesti esim. mvn clean install komennolla:

<executions>
  <execution>
    <phase>install</phase>
    <goals>
      <goal>upload-source-bundle</goal>
      <goal>create-application-version</goal>
      <goal>update-environment</goal>
    </goals>
  </execution>
</executions>

No niin, tuossa pluginissa on kuitenkin metka bugi, ainakin nykyversiossa. Jostain syystä create-vaiheessa se vaatii cnamePrefix:in parametrina (ja luo tomcat ympäristön joka ei tue edes Java 7:aa vaan on Java 6 tasossa – kamalaa! – mutta ei vielä se bugi) – mutta update vaiheessa taas tuo cnamePrefix kylmästi ohitetaan ja sen sijaan on jotenkin kovakoodattu artifactId:n käyttö.

Toisin sanoen, fiksuinta on tehdä beanstalkkiin ensin sopiva projekti jossa on oikeasti Java 7 tuki (ja saisi se 8:kin tippua sinne jossain vaiheessa..) – ja pitää huolta että cnamePrefix ja projektin artifactId ovat prikulleen, case-sensitiivisesti samat. Silloin piisaa konfiguraatioksi:

<plugin>
  <groupId>br.com.ingenieux</groupId>
  <artifactId>beanstalk-maven-plugin</artifactId>
  <version>1.3.1</version>
  <configuration>
    <applicationName>NewFacebookApp</applicationName>
     <s3Key>${project.artifactId}/${project.build.finalName}-${maven.build.timestamp}.war</s3Key>
     <s3Bucket>elasticbeanstalk-eu-west-1-1112223333</s3Bucket>
     <region>eu-west-1</region>
     <environmentName>NewFacebookEnvironment</environmentName>
  </configuration> 
</plugin>

Ja tarkista siis, että projektin alussa määritelty artifactId = beanstalkissa oleva url prefix osa, eli se mitä on ennen elasticbeanstalk.com osaa. Sitten toimii.

Eli ei ihan vakaata kamaa tämä plugin. Mutta toimiessaan ihmeellisen ihana.

(kirosanat editoitu pois konfiguraation kommenteista)

 

 

Spring Boot + Maven = Anna valaistunutta kenkää!

Oh dear, mahtaako tuo sanaleikki aueta kenellekään? Ainakin syytä tarkistaa wikipedia 😉

Kaksi lempivimpaintani, Spring ja Maven, ovat monessa projektissa pohjana. Spring on menossa suuntaan jossa pyritään helpottamaan sen aloitusta ja projekti on nimeltään Spring boot. Pistän tähän taas muistiinpanoja kokeiluistani.

Disclaimer: Vaikka Spring on monen mielestä legacymoskaa ja puhdas Java EE vaihteeksi pop, se tarjoaa edelleenkin muutaman houkuttavan argumentin ketterän projektin kannalta – kuten kyky asentaa softa vaikka tomcat palvelimille (joista löytyy esim. Amazon klusterista helpot valmisasennukset) – kyky ymmärtää ja hallita mekanismeja eri tavalla kuin sovelluspalvelinten mystiset automatiikat toimivat – ja ennen kaikkea mahtava tuki hollywood principlelle ja aop:lle – joka taas tarjoaa hienosti isolaatiota ja sitä myöden yksikkötestattavuutta, sitä myöden nopeita testejä, sitä myöden kehittäjätestejä ja testikattavuutta – sitä myöden speksejä ja toimintavarmuutta – sitä myöden business arvoa. Eli jeah, ei Springin hautajaisia vielä ole pidetty.

Vaihe 1: Tarvitaan kansio projektille, ja pom.xml. Tämmöinen:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 <modelVersion>4.0.0</modelVersion>
<groupId>com.whatever</groupId>
 <artifactId>myproject</artifactId>
 <version>0.0.1-SNAPSHOT</version>
<parent>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-parent</artifactId>
 <version>1.1.0.BUILD-SNAPSHOT</version>
 </parent>
 <!-- (you don't need this if you are using a .RELEASE version) -->
<repositories>
 <repository>
 <id>spring-snapshots</id>
 <url>http://repo.spring.io/snapshot</url>
 <snapshots>
 <enabled>true</enabled>
 </snapshots>
 </repository>
 <repository>
 <id>spring-milestones</id>
 <url>http://repo.spring.io/milestone</url>
 </repository>
 </repositories>
 <pluginRepositories>
 <pluginRepository>
 <id>spring-snapshots</id>
 <url>http://repo.spring.io/snapshot</url>
 </pluginRepository>
 <pluginRepository>
 <id>spring-milestones</id>
 <url>http://repo.spring.io/milestone</url>
 </pluginRepository>
 </pluginRepositories>
</project>

 

Jep, ja vertavuotavalla terällä tässä mennään. Eli bootin epävirallisella snapshot versiolla joka on liikkeessä. Mutta mielummin terällä kuin terän alla.

Päräytetäänpä tässä vaiheessa mvn package. Hmm tottakai esivaatimuksena Java asennettuna (8 tottakai), ja Maven, ja molemmat komentopolussa. Tai voit avata tämän projektin vaikka Netbeans Open toiminnolla, jossa pom tiedostoa onkin mukavampi editoida.

Tämä ei vielä paljoa tee. Lisätään web dependency:

<dependencies>
 <dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-web</artifactId>
 </dependency>
 </dependencies>

Aja uudelleen mvn package. Nyt paukkuu vähän enemmän latauksia – verkosta ladataan perus-spring-web projektin tarvittavat riippuvuudet. Niitä on paljon (Spring jar helvetti). Ja hups, tässä tuli mukana myös Tomcat.

Päräytetään REST palvelu paikalleen. Naputa tai kopioi tämä maven lähdekoodikansioon (src/main/java – joudut varmaan luomaan tämän kansiorakenteen, koska boot ei sitä automaattisesti tee. Ja Mavenhän vaatii Javan olevan juuri tuon polun alla. Saa tehdä paketin jos erityisesti kutkuttaa.):

import org.springframework.boot.*;
import org.springframework.boot.autoconfigure.*;
import org.springframework.stereotype.*;
import org.springframework.web.bind.annotation.*;

@RestController
@EnableAutoConfiguration
public class Example {

    @RequestMapping("/")
    String home() {
        return "Hello World!";
    }

    public static void main(String[] args) throws Exception {
        SpringApplication.run(Example.class, args);
    }

}

Nyt sitten nopeaan testaukseen. Tarkista ensin ettei ole käynnissä palvelimia portissa 8080 (sammuta Glassfish 😉

Aja seuraava komento:

mvn spring-boot:run

Tämä potkaisee käyntiin tomcatin ja voit kokeilla sitä selaimella osoitteessa http://localhost:8080/

Toimiiko? Mahtavaa, en olisi uskonut. REST servicehän se sieltä kurkkii.

Serverin voi ajaa alas Ctrl+C

Paketointi

Nyt voisi paukauttaa koko roskan-tomcatteineen kaikkineen boot .jar pakettiin. Lisää dependencies-osan alle tällainen plugin:

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

Ja aja komentoriviltä:

maven package

Tadaa! target-kansioon syntyy uusi .jar joka sisältää tarvittavat riippuvuudet, voit ajaa sen komentoriviltä näin:

java -jar target/myproject-0.0.1-SNAPSHOT.jar

Kun kyllästyt leikkimään, Ctrl+C lopettaa taas.

Spring Boot Java taso on oletuksena 6. Voisi olla kiva käytellä Java 8 uusia piirteitä. Se onnistuu näin pom.xml:ssä:

<properties>
    <java.version>1.8</java.version>
</properties>

Ja jos haluat käyttää myös Tomcat 8 versiota 7:n sijasta:

 <properties>
 <java.version>1.8</java.version>
 <tomcat.version>8.0.3</tomcat.version>
 </properties>

Yksi suositeltava parannus on jakaa initialisointi ja toiminnot eri tiedostoihin, voit tehdä esim. Application-tiedoston sovelluksesi pakettihierarkiaan tähän tapaan:

package com.myorg.myproj;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@EnableAutoConfiguration
@ComponentScan
public class Application {

  public static void main(String[] args) {
    SpringApplication.run(Application.class, args);
  }

}

Ja näin tuo Example-luokka (joka olisi hyvä olla pakettihierarkiassa em. Application luokan alla) näyttäisi tältä:

package com.myorg.myproj.hello;

import org.springframework.boot.*;
import org.springframework.boot.autoconfigure.*;
import org.springframework.stereotype.*;
import org.springframework.web.bind.annotation.*;

@RestController
public class Example {

  @RequestMapping("/")
   String home() {
    return "Hello World2!";
  }

}

Näin on helpompaa lisäillä uusia palveluita.

 

Yhden .jar paketin sovellus on melko kutkuttava, mutta käytännössä on tilanteita joissa olisi myös kiva saada .war ulos, tässä siihen vinkki:

Lisää pom.xml tiedostoon:

<packaging>war</packaging>

Nyt on syytä samantien muuttaa tomcat riippuvuuden scope moodiin ’provided’ – muuten se paketoidaan myös .war paketin sisään toiseen kertaan. Jos sinulla ei ole tätä riippuvuutta vielä, sen voi silti lisätä provided-scopella dependencies-kohtaan:

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
            <scope>provided</scope>
        </dependency>

Tässä kohtaa voisit tehdä clean&build ja tiputella tomcat webapps-kansioon, mutta eipä toimi vielä. Alunperäisen ohjeen luokka nimittäin käytti main-metodia initialisointiin, joka sopii kyllä .jar pakettiin hienosti – mutta jota ei enää kukaan kutsu.

Joten lisäämme Servlet-luokan joka perii SpringBootServletInitializer-luokan, ja nykäisee Application-luokan taas käyntiin:

package com.whatever;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.web.SpringBootServletInitializer;

public class ApplicationInitializer extends SpringBootServletInitializer {

  @Override
  protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
    return application.sources(Application.class);
  }
}

Ja sitten vain mvn clean package, ja target-kansioon putkahtaa geneerinen .war jonka voi asentaa eri palvelimille.

Aika rouheita lisäosia ovat mm. JPA, Spring Data, MongoDB moduulit..

Ja enkunkielinen lähde – kannattaa ehdottomasti lukea esim. pykälä 13.2 koskien pakettien ja luokkien järjestelyn parhaita käytäntöjä.

http://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#getting-started-installing-spring-boot