Vuosi lähestyy loppuaan – Scalaa ja App Engineä ;)

Jep, joululomailut ovat vähentäneet blogikirjailua mutta vielä ehtii yhden artikkelin luikkaamaan sisään ennen kuin vuosi ja kujeet vaihtuvat. Olkoonkin sisällöltään vähän kevyempää tällä kertaa – ei esim. koodi tai konfiguraatiopätkiä tarjolla 😉 Vuoden vaihtumisen kunniaksi uusi ulkoasu kehiin.

Jouduin valitettavasti luopumaan suunnitelmastani toteuttaa google app engine sovellukseen käyttöliittymä Vaadin frameworkillä. Tähän päätökseen vaikutti useampikin asia, mutta se mitä en pystynyt kiertämään oli se että joka vaiheessa oli vastaan tapeltavaa ja säädettävää, ja työaika meni virittelyyn sensijaan että se olisi mennyt ratkaisun tekemiseen. Tämä on aina omassa mielessäni se killswitch jolloin on aika vaihtaa tekniikkaa. Vaadin on itsessään loistava framework, mutta se abstraktoi liiankin korkealle tasolle, jolloin gae:n asettamien vasteaika ja kirjastorajoitusten kanssa tulee vastaan paljon haasteita. Lisäksi tietysti mehukkaimmat komponentit ovat AGPL lisenssin takana josta en järin itse pidä – se kun pakottaa julkaisemaan KOKO oma sovellus lähdekoodina, ei pelkästään muutoksia komponentin koodiin. Tästä pääsee tietysti eroon lisensoimalla komponentin mutta se vaatii tiettyä sitoutumista ratkaisuun. Lopulta ongelmana on myös debuggaus; Testaus pilvessä ei anna aina kovin hyviä virhetietoja ja statistiikkaa – testaus omassa koneessa ei taas toimi samoin kuin pilvessä. Tämä on tietysti vastassa joka frameworkillä mutta kun puolet asioista tapahtuu javascriptin puolella on asiassa vielä lisähaasteita.

Sinnikkäämmät sielut ovat saaneet homman toimimaan, eli tässä pari viitettä gae + vaadin asioihin:

http://vaadin.com/wiki/-/wiki/Main/Google%20AppEngine%20HOWTO

http://www.streamhead.com/maven-spring-vaadin-appengine/

http://devrimyasar.com/blog/2009/11/22/finally-there-vaadin-godsend-on-gwt-google-app-engine/

Eli vielä kerran, Vaadin on hieno ellei peräti loistava framework, ja paljon käytetty, mutta pilvi + vaadin on toistaiseksi early adopter aikaa, jossa joutuu maksamaan aika kovan hinnan etenemisestään. Se on huimasti parempi vaihtoehto kuin paljas Google Web Toolkit, koska suurin osa prosessoinnista ja tilanhallinnasta tapahtuu palvelinpäässä (tästä on myös tietoturvahyötyä GWT:hen verrattuna). Kokonaisuus tulee varmasti tulevaisuudessa paranemaan vielä ennestään, mutta itselleni valikoitui käyttöliittymään RichFaces toistaiseksi, se kun on pilvivalmis uusimmassa 4.0 releasessaan. Näin pääsen hyödyntämään rikkaita JSF kontrolleja ja voin hallita miten paljon AJAX:ia haluan upotella mukaan. So far so good..

Ja sitten, sain valmiiksi koulutusmateriaalin Scala ohjelmoinnista, ja tutustuessa tähän kieleen täytyy sanoa että se on kuin kevättuulen virkistävä hengähdys. Javan vanhuus ja muuttumattomuus näkyy ja useat parannukset ovat kuin laastarilappuja alustan päällä. Se on edelleen kova alusta jolla voi tehdä melkein mitä vain, mutta aika ajoin koodaajalle tekee hyvää tuulettaa vähän ajatuksiaan ja oppia jotain uutta, ja Scala on kyllä miellyttävää. Monet asiat jotka vaativat paljon työtä Javassa ovat salamannopeita Scalassa, ja jokainen koodaaja voi itse päättää haluaako kirjoittaa Scalaa oliomaisesti, vai funktionaalisesti, ja missä suhteissa.

On toki vielä useita alueita joissa Scalassa on keskeneräisiä ratkaisuja ja turhankin monta vaihtoehtoa, mutta suuri rikkaus on myös hypätä saumattomasti Java kirjastoihin aina kun sille on tarve – ja enemmän ja enemmän asioita voi jo nykyisin tehdä scalamaisesti, kuten esim. tiedostojen ja url resurssien käsittelyn, tietokannankäsittelyn, ja web kerrokset. Ja mikä mukavinta, scalaa voi ajaa missä vain java alustalla – esim. pilvessä 😉

Joka tapauksessa, kurssi on näkyvillä http://www.tieturi.fi/avoimet-kurssit/kurssi.html?cat_id=117445444&course_id=83926464 – ja palautetta kuulen mieluusti. Jos homma kiinnostaa niin jatko-osia tulee perässä!

Joka tapauksessa, tämä on viimeinen blogi tälle vuodelle. Parhaimmat uudenvuoden toivotukset kaikille tälle sivuille osuville!

Google App Engine pilvipalvelu ja Java EE 6, osa 4

Silmäilin mielenkiinnolla miten Java EE 7 tulee koskemaan pääteemana pilvipalveluita; Tekisi hyvää saada yhteistä rajapintaa myös tämän osalta, jolloin ainakin teoriassa pilvipalveluista tulisi vapaammin siirettäviä ja kilpailutettavia, ja vältettäisiin nykyinen vahva vendor lock-in.. Aika näyttää miten tässä käy.

Itse päädyin tekemään muutoksen oman pilvipalveluni arkkitehtuuriin. Siinä missä homma alkoi Solakka Java-periaatteella, totesin että vaikka JSF 2 on paljon parannettu versio aiemmasta, tekniikkademosta saa paljon mielenkiintoisemman jos korvaan sen RESTful web service tekniikalla ja suomalaisella Vaadin AJAX frameworkillä.

Valitsin toteutusalustaksi referenssitoteutuksen eli Jersey JAX-RS alustan. Sain sen käyttöön Mavenin  pom.xml:ssä näin:

<!-- Turn on Jersey JAX-RS -->
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-server</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-client</artifactId>
<version>1.4</version>
</dependency>
 
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-json</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>com.sun.jersey.contribs</groupId>
<artifactId>jersey-multipart</artifactId>
<version>1.4</version>
</dependency>

Eli muutama dependency lisää. Lisäksi web.xml:ssä piti ottaa Jersey servlet käyttöön ja kertoa mitä pakettia skannata resurssien osalta, tähän tapaan:

<!-- JERSEY support -->
<servlet>
<servlet-name>Jersey Web Application</servlet-name>
<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>fi.tieturi.pilvenveikko.services</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Jersey Web Application</servlet-name>
<url-pattern>/resources/*</url-pattern>
</servlet-mapping>

Ja siitä se sitten lähti. REST filosofian mukaan muokkaan palveluita CRUD tyyliin joten jatkossa voi luoda tätä kautta WorkUnit, Person, ja Goal yksiköitä, hakea ja päivittää ja poistaakin niitä, http url osoitteilla.

Pari pikku knoppiakin tuli vastaan. Google App Enginen JPA toteutus ei haekaan tietoja aina heti vaan käyttää lazy load periaatetta. Näin on tarpeen varmistaa että kaikki tiedot on kannasta saatu ennen kuin niitä xml:ksi marshalloidaan . Tämän voi hoitaa vaikkapa size-metodia kutsumalla. Tässä maistiainen Resource luokastani:

package fi.tieturi.pilvenveikko.services;
import fi.tieturi.pilvenveikko.domain.WorkUnit;
import fi.tieturi.pilvenveikko.util.EMF;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
@Path("/workUnit")
public class WorkUnitResources {
  @GET
@Path("/findAll")
@Produces("text/xml")
public List<WorkUnit> getWorkUnits() {
EntityManager em = null;
try {
em = EMF.get().createEntityManager();
Query q = em.createQuery("SELECT wu FROM WorkUnit wu ORDER BY wu.workDate");
q.setMaxResults(200); // limit to max 200 results
List<WorkUnit> results = q.getResultList();
results.size(); // this is a hack to wait until results are all there
return results;
} finally {
if (em != null) {
em.close();
}
}
 }

@GET
@Path("/read/{workUnitId}")
@Produces("text/xml")
public WorkUnit getWorkUnitById(@javax.ws.rs.PathParam(value="workUnitId") long workUnitId) {
EntityManager em = null;
try {
em = EMF.get().createEntityManager();
WorkUnit result = em.find(WorkUnit.class, workUnitId);
return result;
} finally {
if (em != null) {
em.close();
}
}
}
}

Ja siinä se. Ensi kerralla voisin jutella vaikkapa Vaadin frameworkin käyttöönotosta ja piirteistä ja pilvivirityksistä.

Google App Engine pilvipalvelu ja Java EE 6, osa 3

No niin, huomasin etten ole mennyt paljonkaan yksityiskohtiin näissä pilvipalveluissa, eli ihan omaksi muistintueksikin, tässä yksityiskohtia. Varoitus, tästä tulee rumaa, kuten aina kun yksityiskohtiin mennään.

Toimiva Maven pom jossa on mukana JPA 1 ja JSF 2, on tässä:

<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/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>fi.tieturi</groupId>
  <artifactId>PilvenVeikko</artifactId>
  <packaging>war</packaging>
  <version>1.0-SNAPSHOT</version>
  <name>PilvenVeikko Java EE 6 Webapp</name>
  <url>http://maven.apache.org</url>
  <repositories>
    <repository>
      <id>java.net2</id>
      <name>Repository hosting the jee6 artifacts</name>
      <url>http://download.java.net/maven/2</url>
    </repository>
  </repositories>
  <pluginRepositories>
    <pluginRepository>
      <id>maven-gae-plugin-repo</id>
      <name>maven-gae-plugin repository</name>
      <url>http://maven-gae-plugin.googlecode.com/svn/repository</url>
    </pluginRepository>
  </pluginRepositories>
  <dependencies>
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>servlet-api</artifactId>
      <version>2.5</version>
      <scope>provided</scope>
    </dependency>
    <dependency>
      <groupId>javax.servlet.jsp</groupId>
      <artifactId>jsp-api</artifactId>
      <version>2.1</version>
      <scope>provided</scope>
    </dependency>
    <dependency>
      <groupId>com.sun.faces</groupId>
      <artifactId>jsf-api</artifactId>
      <version>2.0.2</version>
    </dependency>
    <dependency>
      <groupId>com.sun.faces</groupId>
      <artifactId>jsf-impl</artifactId>
      <version>2.0.2</version>
      <type>jar</type>
    </dependency>
    <dependency>
      <groupId>org.glassfish.web</groupId>
      <artifactId>el-impl</artifactId>
      <version>2.2</version>
    </dependency>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.8.1</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.datanucleus</groupId>
      <artifactId>datanucleus-jpa</artifactId>
      <version>1.1.5</version>
      <type>jar</type>
    </dependency>
    <dependency>
      <groupId>javax.persistence</groupId>
      <artifactId>persistence-api</artifactId>
      <version>1.0</version>
      <type>jar</type>
    </dependency>
    <dependency>
      <groupId>org.datanucleus</groupId>
      <artifactId>datanucleus-core</artifactId>
      <version>1.1.5</version>
      <type>jar</type>
    </dependency>
    <dependency>
      <groupId>javax.jdo</groupId>
      <artifactId>jdo2-api</artifactId>
      <version>2.3-eb</version>
      <type>jar</type>
    </dependency>
    <dependency>
      <groupId>org.datanucleus</groupId>
      <artifactId>datanucleus-enhancer</artifactId>
      <version>1.1.4</version>
      <type>jar</type>
    </dependency>
    <dependency>
      <groupId>org.datanucleus</groupId>
      <artifactId>datanucleus-rdbms</artifactId>
      <version>1.1.5</version>
      <type>jar</type>
    </dependency>
    <dependency>
      <groupId>com.google</groupId>
      <artifactId>appengine-local-runtime-shared</artifactId>
      <version>1.2.0</version>
      <type>jar</type>
    </dependency>
    <dependency>
      <groupId>org.datanucleus</groupId>
      <artifactId>datanucleus-appengine</artifactId>
      <version>1.0.7.final</version>
      <type>jar</type>
    </dependency>
    <dependency>
      <groupId>com.google</groupId>
      <artifactId>appengine-sdk-api</artifactId>
      <version>1.3.8</version>
      <type>jar</type>
    </dependency>
  </dependencies>
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>2.0.2</version>
        <configuration>
           <source>1.6</source>
          <target>1.6</target>
       </configuration>
    </plugin>
    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-war-plugin</artifactId>
        <version>2.1-beta-1</version>
        <configuration>
          <failOnMissingWebXml>false</failOnMissingWebXml>
        </configuration>
    </plugin>
    <plugin>
        <groupId>net.kindleit</groupId>
        <artifactId>maven-gae-plugin</artifactId>
        <version>0.6.0</version>
       <configuration>
          <sdkDir>D:/java/appengine-java-sdk-1.3.8</sdkDir>
        </configuration>
      </plugin>
      <plugin>
        <groupId>org.datanucleus</groupId>
        <artifactId>maven-datanucleus-plugin</artifactId>
        <version>1.1.4</version>
        <configuration>
          <mappingincludes>**/domain/*.class</mappingincludes>
          <verbose>true</verbose>
          <enhancername>ASM</enhancername>
          <api>JPA</api>
        </configuration>
        <executions>
          <execution>
            <phase>compile</phase>
            <goals>
              <goal>enhance</goal>
            </goals>
          </execution>
      </executions>
      <dependencies>
        <dependency>
          <groupId>org.datanucleus</groupId>
          <artifactId>datanucleus-core</artifactId>
          <version>1.1.5</version>
          <exclusions>
           <exclusion>
              <groupId>javax.transaction</groupId>
              <artifactId>transaction-api</artifactId>
            </exclusion>
          </exclusions>
        </dependency>
        <dependency>
          <groupId>org.datanucleus</groupId>
          <artifactId>datanucleus-rdbms</artifactId>
          <version>1.1.5</version>
        </dependency>
        <dependency>
          <groupId>org.datanucleus</groupId>
          <artifactId>datanucleus-enhancer</artifactId>
          <version>1.1.4</version>
        </dependency>
      </dependencies>
      </plugin>
    </plugins>
    <finalName>PilvenVeikko</finalName>
  </build>
  <profiles>
    <profile>
      <id>netbeans-private</id>
      <activation>
        <property>
          <name>netbeans.execution</name>
          <value>true</value>
        </property>
      </activation>
      <properties>
        <netbeans.deployment.server.id>[C:\Program Files (x86)\glassfish-3.0.1\glassfish]deployer:gfv3ee6:localhost:4848</netbeans.deployment.server.id>
      </properties>
    </profile>
  </profiles>
  <properties>
    <netbeans.hint.deploy.server>gfv3ee6</netbeans.hint.deploy.server>
  </properties>
</project>

Julkaisin tuon kokonaisena copy-pastea ajatellen. Pieni mutta tärkeä kohta on lopussa oleva DataNucleus enhancer plugin, koska DataNucleus käyttää vielä vanhanaikaista bytecode vahvistusta, tuo vaihe tehdään käännöksen jälkeen, tärkeää tarkistaa että pakettirakenne-parametri on oikein. Vierastan lisä ant scriptien käyttöä joten yritin tehdä kaiken Mavenillä ja näyttäisi toimivan.

Toinen jippo oli imaista gae kirjastot paikalliseen maven repositoryyn etukäteen, koska tietääkseni niitä ei globaaleista repositoryistä löydy. Kirjastot mitä näemmä tarvittiin olivat datanucleus-jpa, datanucleus-core, jdo2-api, datanucleus-enhancer, datanucleus-rdbms, appengine-local-runtime-shared, datanucleus-appengine, sekä appengine-sdk-api. Lisäksi javan core kirjastoista tarvitsin persistence-api ja servlet-api ja jsp-api pakettien lisäksi jsf-api ja jsf-impl kirjastot, versio 2, sekä el-impl. Huom! JSF-impl on google app enginelle tuunattu versio, johtuen parista rajoituksesta sen suhteen. Tässä pari linkkiä siihen:

Take a look at this site
http://javadocs.wordpress.com/2009/10/17/mojarra-jsf-2-0-rc2-and-google-app-engine-sdk-1-2-6/

and download this file
http://code.google.com/p/joshjcarrier/source/browse/trunk/Sun%20JSF%20GAE/jsf-impl-gae.jar

Rakensin tällaisen scriptin joka vie ne repositoryyn:

call mvn install:install-file -Dfile=lib/appengine-tools-api.jar -DgroupId=com.google -DartifactId=appengine-tools -Dversion=1.2.0 -Dpackaging=jar -DgeneratePom=true
call mvn install:install-file -Dfile=lib/shared/appengine-local-runtime-shared.jar -DgroupId=com.google -DartifactId=appengine-local-runtime-shared -Dversion=1.2.0 -Dpackaging=jar -DgeneratePom=true
call mvn install:install-file -Dfile=lib/user/appengine-api-1.0-sdk-1.3.8.jar -DgroupId=com.google -DartifactId=appengine-sdk-api -Dversion=1.3.8 -Dpackaging=jar -DgeneratePom=true
call mvn install:install-file -Dfile=lib/user/orm/datanucleus-appengine-1.0.7.final.jar -DgroupId=org.datanucleus -DartifactId=datanucleus-appengine -Dversion=1.0.7.final -Dpackaging=jar -DgeneratePom=true
call mvn install:install-file -Dfile=lib/user/orm/datanucleus-core-1.1.5.jar -DgroupId=org.datanucleus -DartifactId=datanucleus-core -Dversion=1.1.5 -Dpackaging=jar -DgeneratePom=true
call mvn install:install-file -Dfile=lib/user/orm/datanucleus-jpa-1.1.5.jar -DgroupId=org.datanucleus -DartifactId=datanucleus-jpa -Dversion=1.1.5 -Dpackaging=jar -DgeneratePom=true
call mvn install:install-file -Dfile=lib/user/orm/jdo2-api-2.3-eb.jar -DgroupId=org.datanucleus -DartifactId=jdo2-api -Dversion=2.3 -Dpackaging=jar -DgeneratePom=true
call mvn install:install-file -Dfile=lib/user/orm/geronimo-jpa_3.0_spec-1.1.1.jar -DgroupId=org.geronimo -DartifactId=geronimo.jpa -Dversion=1.1.1 -Dpackaging=jar -DgeneratePom=true
call mvn install:install-file -Dfile=lib/user/orm/geronimo-jta_1.1_spec-1.1.1.jar -DgroupId=org.geronimo -DartifactId=geronimo.jpa -Dversion=1.1.5 -Dpackaging=jar -DgeneratePom=true
call mvn install:install-file -Dfile=jsf-impl-gae.jar -DgroupId=com.sun.faces -DartifactId=jsf-impl -Dversion=2.0.2 -Dpackaging=jar -DgeneratePom=true

No niin , ehkäpä siinä oli taas tarpeettoman paljon tietoa tällä kertaa 😉 Joka tapauksessa, itselleni muistiin mitä askelia tarvittiin pilveen pääsemiseen.