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.

Google App Engine pilvipalvelu ja Java EE 6, osa 2

Jahas, seuraava episodi pilvipalveluiden opiskelussa. Virittelin Maven 3 projektin siten että sen ohella että pilvipalveluiden Hello World onnistuu, myös JSF 2.0 ja JPA on tuettuna. Näin on mahdollista tehdä Solakka Java-periaatteiden mukaan hyvinkin nopeasti ja helposti yksikkötestattavaa sovelluskoodia. Samalla kävi ilmi myös Google App Engine JPA:n rajoitukset:

– JPA 1.0 on tuettuna, JPA 2.0 ei ole (ainakaan nyt). Erona on lähinnä piirteet joita en muutenkaan käyttäisi, Criteria Query on yksi niistä.
– Alla oleva DataNucleus engine EI ole relaatiotietokanta, vaan se on NoSQL kanta, joten kaikki JPA piirteet eivät toimi ollenkaan, esim.
– Many-to-Many ei toimi,
– Join kyselyt eivät toimi (lapsi-entiteetin kenttiä ei voi käyttää hakuehtoina),
– group by, having, sum, avg, max, min ei toimi
– polymorfiset kyselyt eivät toimi

Aika tyly lista rajoituksia, siis. Kuitenkin.. Kaikki nuo piirteet tulevat relaatiokantateorian puolelta, ja tässä meillä on uuden ajan kanta jota käytetään uudella tapaa. Aika näyttää riittääkö siinä rahkeet, mutta hieman pitää suunnittelua miettiä tarkemmin.

Aluksi oli hieman ongelmia sen kanssa että mitä kirjastoja tarvitaan, ja puuttuvat kirjastot antoivat kryptisiä virheilmoituksia. Hyvä myös huomata että  DataNucleus käyttää bytekoodin vahvistamista, eli tarvitaan post-processor plugin maveniin kääntäjän lisäksi. Lisäksi CDI:n ja injektioiden sijaan pelasin toistaiseksi varman päälle ja en käyttänyt niitä, vaan EntityManagerFactory saadaan singletonilta, ja annotaatioissa käytin JSF:n omia ManagedBean jne annotaatioita, enkä CDI:n Named ja Inject annotaatioita. Jatkossa tarkoitus muokata ympäristöstä parempaa, mutta nyt se näyttäisi toimivan kivasti, ja App Enginestä löytyy jopa työkalut joilla voi tarkastella logeja ja tietokannan sisältöä.

Lueksin että Spring framework toimii ongelmattomammin App Enginessä, ja JDO on JPA:ta luontevampi rajapinta tässä tapauksessa, kun kanta ei olekaan relaatiokanta. Mutta itselleni tärkeämpää on standardoidut rajapinnat ja ydinstandardit ohi kolmannen osapuolen frameworkien, eli katsotaan miten tässä käy.

Google App Engine pilvipalvelu ja Java EE 6, osa 1

Olen aivan pilvessä.

No niin, vaikka Pilvi on uusi trendi, tuo on jo vitsinä vanha. Joka tapauksessa, tänään oli hetkonen aikaa kokeilla Google App Engine viritystä vähäsen, olen jo aiemmin sinne tehnyt palveluita mutta nyt ehti hetken kokeilla ajatuksella.

Google App Engine ideana on hostata ympäristö josta voi ostaa skaalautuvasti prosessointitehoa ja muistia ja levytilaa, niinsanotusti vääntöä. Palvelusta löytyy myös tietokantaratkaisuja, ja tukea löytyy useammalle ohjelmointikielelle. Liikkeellelähtö on ilmaista, mutta jos kapasiteetin tarpeet kasvavat, skaalautuvuutta saa rahalla.

Minua tietysti kiinnosti miten uudet Java EE 6 solakat ohjelmointitekniikat siellä pelaavat, ja pelasivathan ne, pienin virityksin. Sain iltapäivän aikana Maven vetoisen projektin nykäistyä kasaan, sinne tuki EE 6 piirteille kuten JSF 2, ja tietysti Netbeans editorina. Mitään kovin ihmeellistä en ole vielä koonnut palveluun mutta tämä on silti hyvä alku. Tuli peräti lämmin tunne kun omin silmin näki miten jsf softa jota hetki sitten oli ajanut omalla koneellaan pyöri nyt hostattuna Googlen myllytyksessä. Ja niillä pojilla muuten myllyä piisaa.

Tämä oli alku, ja jatkossa aion kytkeä CDI mekanismit päälle, tutkia miten JPA 2 alustalla pelaa, ja tehdä sinne jonkun sovelluksen. Tällä hetkellä kallistuisin työajanseurannan puoleen, ne ovat aina hätäisesti tehtyjä virityksiä ja tässä olisi tilaisuus tehdä helposti skaalattava softa suht vähällä vaivalla ja pistää pilvi koetukselle. Katsotaan miten käy. Jatko-osia siis tulossa jahka löydän lisää aikaa.