Homepage email me grab rss 2.0 feed

Archiv für Schublade Code

Persistenz mit JPA und Google App Engine

Google App EngineJPA (Java Persistence API) ist der aktuelle Java-Standard für Persistenz und kann neben JDO (Java Data Objects) zum Zugriff auf den Google App Engine Datastore genutzt werden. Die JPA Implementierung basiert dabei auf der DataNucleus Access Platform. Da der Datastore allerdings keine klassische relationale Datenbank ist, gibt es leider auch einige nicht unterstützte Featurs von JPA, die bei der Entwicklung berücksichtigt werden müssen.

NetBeansAusgangspunkt für das folgende Tutorial ist das NetBeans Projekt aus dem JSF 2.0 mit NetBeans 6.8 und Google App Engine Tutorial. Das Tutorial ist dabei wieder bewusst detailliert gehalten, um neben der Implementierung für die Google App Engine auch möglichst viele nützliche Features und Wizards von NetBeans vorzustellen.

1. Erstellen der Persistence Unit
Durch einen Rechtsklick auf das Projekt und “New > Other…” und dann “Persistence > Persistence Unit” wird diese für das Projekt erstellt. Leider erlaubt es der NetBeans Wizard nicht, eine Persistence Unit ohne Database Connection zu erstellen. Daher in der Dropdown-Liste für Database Connection einfach eine bestehende Connection (z.B. “sample”) auswählen und “Finish” klicken.

Erstellen der PersistanceUnit mit NetBeans
Trotz dieser “falschen” Angabe ist die durch das Google App Engine Plugin erstellte Persistence Unit (fast) korrekt. Nur die Zeile “<exclude-unlisted-classes>false</exclude-unlisted-classes>” hat bei mir im weiteren Verlauf folgende Exception verursacht und sollte daher entfernt werden.

java.lang.IllegalArgumentException: Type ("de.alteskind.appengine.jpa.Message") is not that of an entity but needs to be for this operation
 
Caused by: org.datanucleus.exceptions.NoPersistenceInformationException: The class "de.alteskind.appengine.jpa.Message" is required to be persistable yet no Meta-Data/Annotations can be found for this class. Please check that the Meta-Data/annotations is defined in a valid file location.

Die korrekte Persistence Unit ist in folgendem Listing aufgeführt:

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.0"
  xmlns="http://java.sun.com/xml/ns/persistence"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
  http://java.sun.com/xml/ns/persistence/
  persistence_1_0.xsd">
  <persistence-unit name="GoogleAppEngine_JSF20PU"
    transaction-type="RESOURCE_LOCAL">
    <provider>
      org.datanucleus.store.appengine.jpa.
      DatastorePersistenceProvider
    </provider>
    <properties>
      <property name="datanucleus.ConnectionURL"
        value="appengine"/>
      <property name="datanucleus.NontransactionalRead"
        value="true"/>
      <property name="datanucleus.NontransactionalWrite"
        value="true"/>
    </properties>
  </persistence-unit>
</persistence>

2. Implementieren der Entity Klasse
In unserem Beispiel erstellen wir durch einen Rechtsklick auf das Projekt und “New > Entity Class…” eine einfache Entität mit dem Namen “Message”. Als Package kann z.B. “de.alteskind.appengine.jpa” angegeben werden. Zu dem bereits bestehenden Feld “id” werden nun noch die Felder “message” (String), “ip” (String) und “dateTime” (java.util.Date) hingzugefügt. Die Getter und Setter können durch einen rechten Mausklick in den Editor und dann “Insert Code… > Getter and Setter…” generiert werden.
NetBeans zeigt nun im Editor für das Feld dateTime einen Fehler (“A temporal attribute must be marked with the @Temporal attribute”) an. Durch einen Klick auf den Fehler kann mit “Create @Temporal annotation” eine passende Annotation erstellt werden. Da in unserem Fall ein Timestamp gesetzt werden soll, könnte als TemporalType auch direkt “TIMESTAMP” gewählt werden. Leider wird das durch die Google App Engine Implementierung von DataNucleus Access Platform nicht unterstützt, so dass der Timestamp später im Code manuell gesetzt werden muss.
Für die Implementierung der Entity Klassen sind wie bereits beschrieben außerdem einige Beschränkungen der Google App Engine JPA Implementierung zu beachten. So wird z.B. in der @GeneratedValue Annotation nur die Strategie “GenerationType.IDENTITY” unterstützt, so dass auch diese Annotation entsprechend angepasst werden muss.

package de.alteskind.appengine.jpa;
 
import java.io.Serializable;
import java.util.Date;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
 
@Entity
public class Message implements Serializable {
 
 private static final long serialVersionUID = 1L;
 
 @Id
 @GeneratedValue(strategy = GenerationType.IDENTITY)
 private Long id;
 
 private String message;
 
 private String ip;
 
 @Temporal(TemporalType.DATE)
 private Date dateTime;
 
 public Long getId() {
  return id;
 }
 
 public void setId(Long id) {
  this.id = id;
 }
 
 public String getIp() {
  return ip;
 }
 
 public void setIp(String ip) {
  this.ip = ip;
 }
 
 public String getMessage() {
  return message;
 }
 
 public void setMessage(String message) {
  this.message = message;
 }
 
 public Date getDateTime() {
  return dateTime;
 }
 
 public void setDateTime(Date dateTime) {
  this.dateTime = dateTime;
 }
 
 @Override
 public int hashCode() {
  int hash = 0;
  hash += (id != null ? id.hashCode() : 0);
  return hash;
 }
 
 @Override
 public boolean equals(Object object) {
  if (!(object instanceof Message)) {
   return false;
  }
  Message other = (Message) object;
  if ((this.id == null && other.id != null) ||
   (this.id != null && !this.id.equals(other.id))) {
   return false;
  }
 return true;
 }
 
 @Override
 public String toString() {
  return "de.alteskind.appengine.jpa.Message"
   + "[id=" + id + "]";
 }
}

3. Implementieren der JPA Controller und Entity Manager Factory Klassen
Nachdem die Entity erfolgreich erstellt wurde, kann nun über einen Rechtsklick auf das Projekt und “New > Other…” und dann “Persistence > JPA Controller Classes from Entity Classes” automatisch eine JPA Controller (=DAO) Klasse für die Entität erstellt werden. Als Package sollte das Package der Message Entität ausgewählt werden.

Erstellen der JPA Controller Klasse mit dem NetBeans Wizard
Diese Klasse ist allerdings nicht direkt für den Einsatz in der Google App Engine geeignet, da sie folgende Exception verursacht:

java.lang.IllegalStateException: Application code attempted to create a EntityManagerFactory named GoogleAppEngine_JSF20PU, but one with this name already exists! Instances of EntityManagerFactory are extremely slow to create and it is usually not necessary to create one with a given name more than once. Instead, create a singleton and share it throughout your code. If you really do need to create a duplicate EntityManagerFactory (such as for a unittest suite), set the appengine.orm.disable.duplicate.emf.exception system property to avoid this error.

Um diesen Fehler zu vermeiden, sollte – wie auch in der offiziellen Google App Engine Dokumentation beschrieben – eine Entity Manager Factory als Singelton implementiert werden.

package de.alteskind.appengine.jpa.utils;
 
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
 
public final class EMF {
 private static final EntityManagerFactory emfInstance =
  Persistence.
  createEntityManagerFactory("GoogleAppEngine_JSF20PU");
 
 private EMF() {
 }
 
 public static EntityManagerFactory get() {
  return emfInstance;
 }
}

Nun kann die durch NetBeans generierte JPA Controller Klasse geändert werden, so dass die Entity Manager Factory verwendet wird. Es muss außerdem die SELECT Abfrage geändert werden, da die Google App Engine Datanucleus Implementierung den Operator “object” nicht unterstützt und die folgende Exception verursacht.

org.datanucleus.store.appengine.query.DatastoreQuery$
UnsupportedDatastoreOperatorException: Problem with query <SELECT object(o) FROM Message as o>: App Engine datastore does not support operator object.

Es tritt außerdem eine Exception auf, wenn versucht wird über das ResultSet des SELECT Abfrage zu iterieren (z.B. bei der Anzeige der Tabelle mit allen Messages) obwohl der EntityManager bereits geschlossen ist.

org.datanucleus.exceptions.NucleusUserException: Object Manager has been closed

Diese Verhalten ist allerdings nicht JPA konform und es existiert dazu auch bereits ein Bugreport. Um das Problem zu umgehen kann z.B. ein neues Object mit dem ResultSet erstellt werden. Außerdem liefert die Query in der “getMessageCount” Methode ein Integer zurück, daher muss der Cast von Long auf Integer geändert werden.

Die korrekte und unter der Google App Engine funktionierende JPA Controller Klasse ist in folgendem Listing aufgeführt.

package de.alteskind.appengine.jpa;
 
import de.alteskind.appengine.jpa.exceptions.
  NonexistentEntityException;
import de.alteskind.appengine.jpa.utils.EMF;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Query;
import javax.persistence.EntityNotFoundException;
 
public class MessageJpaController {
 
 private EntityManagerFactory emf = null;
 
 public MessageJpaController() {
  emf = EMF.get();
 }
 
 public EntityManager getEntityManager() {
  return emf.createEntityManager();
 }
 
 public void create(Message message) {
  EntityManager em = null;
  try {
   em = getEntityManager();
   em.getTransaction().begin();
   em.persist(message);
   em.getTransaction().commit();
  } finally {
   if (em != null) {
    em.close();
   }
  }
 }
 
 public void edit(Message message)
  throws NonexistentEntityException, Exception {
  EntityManager em = null;
  try {
   em = getEntityManager();
   em.getTransaction().begin();
   message = em.merge(message);
   em.getTransaction().commit();
  } catch (Exception ex) {
   String msg = ex.getLocalizedMessage();
   if (msg == null || msg.length() == 0) {
    Long id = message.getId();
    if (findMessage(id) == null) {
     throw new NonexistentEntityException("The message "
      + "with id " + id + " no longer exists.");
    }
   }
   throw ex;
  } finally {
   if (em != null) {
   em.close();
   }
  }
 }
 
 public void destroy(Long id)
  throws NonexistentEntityException {
  EntityManager em = null;
  try {
   em = getEntityManager();
   em.getTransaction().begin();
   Message message;
   try {
    message = em.getReference(Message.class, id);
    message.getId();
   } catch (EntityNotFoundException enfe) {
    throw new NonexistentEntityException("The message "
     +" with id " + id + " no longer exists.", enfe);
   }
   em.remove(message);
   em.getTransaction().commit();
  } finally {
   if (em != null) {
    em.close();
   }
  }
 }
 
 public List findMessageEntities() {
  return findMessageEntities(true, -1, -1);
 }
 
 public List
findMessageEntities(int maxResults,
  int firstResult) {
  return findMessageEntities(false, maxResults,
  firstResult);
 }
 
 private List
findMessageEntities(boolean all,
  int maxResults, int firstResult) {
  EntityManager em = getEntityManager();
  try {
   Query q = em.createQuery("select o from Message as o");
   if (!all) {
    q.setMaxResults(maxResults);
    q.setFirstResult(firstResult);
   }
   return new ArrayList
(q.getResultList());
  } finally {
   em.close();
  }
 }
 
 public Message findMessage(Long id) {
  EntityManager em = getEntityManager();
  try {
   return em.find(Message.class, id);
  } finally {
   em.close();
  }
 }
 
 public int getMessageCount() {
  EntityManager em = getEntityManager();
  try {
   Query q = em.createQuery("select count(o) from Message "
    + "as o");
   return ((Integer) q.getSingleResult()).intValue();
  } finally {
   em.close();
  }
 }
}

4. Implementieren der JSF 2.0 Managed Bean
Nun kann die JSF 2.0 ManagedBean implementiert werden. Dazu wird die aus dem ersten Teil des Tutorial erstellte Klasse “HelloWorld” gelöscht und stattdessen eine Klasse “MessageView” erstellt (siehe auch Teil 1 des Tutorials). Interessant sind dabei insbesondere die Methoden “postMessage”, “deleteMessage”, “getNumberOfMessages” und “getMessages”, welche Methoden der JPA Controller Klasse aufrufen.

package de.alteskind.appengine.jsf;
 
import de.alteskind.appengine.jpa.Message;
import de.alteskind.appengine.jpa.MessageJpaController;
import java.util.Date;
import java.util.List;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;
import javax.faces.component.html.HtmlDataTable;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.servlet.http.HttpServletRequest;
 
@ManagedBean(name = "MessageView")
@RequestScoped
public class MessageView {
 
 private String helloWorld;
 
 private HtmlDataTable dataTable;
 
 private Message message;
 
 private List messages;
 
 private MessageJpaController messageJpaController;
 
 public MessageView() {
  this.helloWorld = "Hello World from JSF 2.0/Facelets "
   + "and JPA on Google AppEngine!";
  this.message = new Message();
  this.messageJpaController = new MessageJpaController();
 }
 
 public void postMessage() {
  // Get HttpServletRequest object
  FacesContext facesContext =
   FacesContext.getCurrentInstance();
  ExternalContext externalContext =
   facesContext.getExternalContext();
  HttpServletRequest request =
   (HttpServletRequest) externalContext.getRequest();
  
  // Set remote host as IP
  message.setIp(request.getRemoteHost());
  
  // Set timestamp
  message.setDateTime(new Date());
  
  // Create message
  messageJpaController.create(message);
 }
 
 public void deleteMessage()
  throws NonexistentEntityException {
  // Get message object
  Message message = (Message) dataTable.getRowData();
  
  // Destroy message
  messageJpaController.destroy(message.getId());
 }
 
 public int getNumberOfMessages() {
  return messageJpaController.getMessageCount();
 }
 
 public String getHelloWorld() {
  return helloWorld;
 }
 
 public void setDataTable(HtmlDataTable dataTable) {
  this.dataTable = dataTable;
 }
 
 public HtmlDataTable getDataTable() {
  return dataTable;
 }
 
 public Message getMessage() {
  return message;
 }
 
 public void setMessage(Message message) {
  this.message = message;
 }
 
 public List
getMessages() {
  return messageJpaController.findMessageEntities();
 }
}

5. Erstellen der JSF/Facelets Seite
Als letzter Schritt wird nun die JSF/Facelets Seite index.html angepasst, so dass der Body folgenden Code enthält:

<h:form>
 <h:outputText value="#{MessageView.helloWorld}" />
 <br /><br />
 <h:outputLabel value="Message:"/>
 <h:inputText value="#{MessageView.message.message}"/>
 <h:commandButton action="#{MessageView.postMessage}"
  value="Post Message"/>
 <br /><br />
 <h:dataTable value="#{MessageView.messages}"
  binding="#{MessageView.dataTable}"
  rendered="#{MessageView.numberOfMessages > 0}"
  var="message" border="1">
  <h:column>
   <f:facet name="header">
    <h:outputText value="ID" />
   </f:facet>
   <h:outputText value="#{message.id}" />
  </h:column>
  <h:column>
   <f:facet name="header">
    <h:outputText value="Message" />
   </f:facet>
   <h:outputText value="#{message.message}" />
  </h:column>
  <h:column>
   <f:facet name="header">
    <h:outputText value="IP" />
   </f:facet>
   <h:outputText value="#{message.ip}" />
  </h:column>
  <h:column>
   <f:facet name="header">
    <h:outputText value="Timestamp" />
   </f:facet>
   <h:outputText value="#{message.dateTime}">
    <f:convertDateTime pattern="dd.MM.yyyy HH:mm" />
   </h:outputText>
  </h:column>
  <h:column>
   <h:commandButton action="#{MessageView.deleteMessage}"
    value="Delete"/>
  </h:column>
 </h:dataTable>
</h:form>

Scrennshot der Beispielanwendung in der lokalen Google AppEngine
Wie die große Anzahl der hier aufgeführten Exceptions zeigt, gilt es bei der Verwendung von JPA in der Google App Engine einiges zu beachten. Ich hoffe mein Blogeintrag hilft diese Probleme zu umgehen. ;-)

Analog zu Teil 1 des Tutorials kann das komplette NetBeans Projekt inkl. WAR-Archiv hier heruntergeladen sowie unter http://2.latest.altes-kind.appspot.com/ direkt aufgerufen werden.

5 Kommentare

JSF 2.0 mit NetBeans 6.8 und Google App Engine

Google App EngineNachdem ich mich in meiner Master-Thesis mit Cloud Computing und JEE befassen werde, habe ich heute meine ersten Versuche mit der Google App Engine unternommen, die ja seit einigen Monaten auch Java unterstützt und unlängst in Version 1.3.0 veröffentlicht wurde.

NetBeansDabei habe ich versucht eine einfache JSF 2.0 Webanwendung für die Google App Engine mit NetBeans 6.8 (wegen dem tollen JSF 2.0 Support) zu implementieren. Da das offzielle Tutorial leider nach der Einrichtung des NetBeans-Plugins abbricht, möchte ich an dieser Stelle ein vollständiges Konfigurations- und Implementierungsbeispiel sowie das abschließende Deployment in die Google App Engine aufzeigen.

1. Einrichten der Entwicklungsumgebung
Um die Google App Engine nutzen zu können, muss man sich natürlich zuerst für einen Account registrieren. Für das JSF 2.0 Beispiel wird außerdem folgende Software benötigt:

1.1. Google App Engine Plugin für NetBeans: Nachdem NetBeans installiert wurde, wird zunächst das Google App Engine Plugin über “Tools > Plugins” installiert. Im Tab “Settings” wird dazu ein neues Update Center mit einem passendem Namen (z.B. “Google App Engine Plugin”) und folgender URL angelegt: http://kenai.com/projects/nbappengine/downloads/download/Latest_NetBeans68/updates.xml. Anschließend können im Tab “Available Plugins” alle verfügbaren Google App Engine Plugins ausgewählt und installiert werden. Nach erfolgreicher Installation sollte das Plugin im Tab “Installed” zu sehen sein.

Installation des Google App Engine Plugins für NetBeans
1.2. Google App Engine Server in NetBeans: Nun kann über “Tools > Server” Google App Engine als Server hinzugefügt werden, wobei das Installationsverzeichnis des Google App Engine SDK 1.3.0 angegeben werden muss. Außerdem kann im Tab “Javadoc” auf das Javadoc Verzeichnis ([GoogleAppEngineSDK]/docs/javadoc) verwiesen werden.

Javadoc Verzeichnis zur Google App Engine Server Konfiguration hinzufügen
1.3. Google App Engine Library in NetBeans: Da der Google App Engine Server die für den Betrieb von JSF 2.0 Webanwendungen notwendigen Libraries nicht mitbringt, muss in NetBeans über “Tools > Libraries” eine neue Library erstellt werden, die dann der Webanwendung (und somit dem WAR-Archiv) hinzugefügt werden kann. Als “Library Name” kann z.B. “GoogleAppEngine_JSF20″ eingegeben werden und bei “Library Type” wird “Class Libraries” ausgewählt. Dieser Library werden dann folgende JARs hinzugefügt: Apache Xalan-J (serializer.jar, xalan.jar, xercesImpl.jar, xml-apis.jar, xsltc.jar) und Unified Expression Language (el-api-1.1.jar, el-impl-1.1.jar).

Erstellen einer Library für Google App Engine in NetBeans
Damit ist die Konfiguration von NetBeans für die Google App Engine erfolgreich abgeschlossen. ;-)

2. Implementierung der JSF 2.0 Webanwendung
Nun kann die eigentliche Webanwendung mit JSF 2.0 und Google App Engine Support implementiert werden.

2.1 JSF Webanwendung für Google App Engine erstellen: Durch “File > New Project…” und der Kategorie “Java Web” und “Web Application” wird der Wizard zum Erstellen einer JEE Webanwendung aufgerufen und ein passender Projektname (z.B. “GoogleAppEngine_JSF20″) vergeben. Anschließend wird als Server “Google App Engine” ausgewählt und als “ContextPath” nur “/” eingetragen.

Server Einstellungen beim Erstellen des Projektes
Im nächsten Schritt wird als Framework “JavaServer Faces” und “JSF 2.0″ ausgewählt. Nachdem das Projekt erstellt wurde, wird durch einen Rechtsklick auf “Libraries” und “Add Library…” die oben erstellte Google App Engine Library dem Projekt hinzugefügt.

2.2 JSF Webanwendung für Google App Engine konfigurieren: Leider gibt es zwischen der aktuellen JSF 2.0 und Google App Engine 1.3.0 noch einige Kompatibilitätsprobleme, welche aber durch Konfigurationseinstellungen relativ einfach behoben werden können. Die dazu erforderlichen Konfigurationsdateien sind im Projekt in NetBeans unter “Configuration Files” zu finden.

In der Datei “web.xml” müssen dazu folgende Zeilen hinzugefügt werden:
<!-- Google App Engine Bug 1506 JSP 2.1 API but 2.0 Implementation -->
<context-param>
  <param-name>com.sun.faces.expressionFactory</param-name>
  <param-value>com.sun.el.ExpressionFactoryImpl</param-value>
</context-param>
<!-- Google App Engine 1.3.0 appears to handle server-side state saving -->
<context-param>
  <param-name>javax.faces.STATE_SAVING_METHOD</param-name>
  <param-value>server</param-value>
</context-param>
<!-- Accommodate Single-Threaded Requirement of Google App Engine -->
<context-param>
  <param-name>com.sun.faces.enableThreading</param-name>
  <param-value>false</param-value>
</context-param>

Die Datei “appengine-web.xml” wird um folgenden Eintrag erweitert:
<!-- Activate Session Support in Google App Engine -->
<sessions-enabled>true</sessions-enabled>

Etwas problematischer ist die Tatsache, dass Anwendungen innerhalb der Google App Engine nur der Zugriff auf bestimmte Klassen der Java Standard Library erlaubt ist. Eine JSF 2.0 Webanwendung testet allerdings beim Start, ob es einen InitalContext aufbauen kann und ruft dazu diese Klasse auf, was dann zu einer Exception innerhalb der Google App Engine führt. Um dies zu verhindern muss die JSF 2.0 Implementierung gepachted werden. Dazu wird im Projekt in Netbeans über einen Rechtsklick auf das Projekt und “New > Java Package” bzw. “New > Java Class” das Package “com.sun.faces.config” und darin eine Klasse mit den Namen “WebConfiguration” und dem Inhalt dieses Links erstellt. Dadurch wird die Implementierung in der jsf-impl.jar durch die gepachte Version überschrieben.

Überschreiben der JSF 2.0 WebConfiguration Klasse um den Zugriff auf InitialContext zu unterbinden

2.3 JSF Webanwendung implementieren:
JSF 2.0 bietet mit der neuen Annotation @ManagedBean die Möglichkeit, eine Klasse direkt als Managed Bean zu deklarieren. Die (umständliche) Konfiguration in einer faces-config.xml wie bei JSF 1.2 ist damit nicht mehr notwendig. Die folgende Klasse zeigt beispielhaft die Verwendung der Annotation:

package de.alteskind.appengine.jsf;
 
import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;
 
@ManagedBean(name = "HelloWorld")
@RequestScoped
public class HelloWorld {
 
 private String message;
 
 public HelloWorld() {
  this.message = "Hello World from JSF 2.0/Facelets"
   + "on Google App Engine!";
 }
 
 public String getMessage() {
  return message;
 }
}

In der Datei index.xhtml kann nun im Body auf die Managed Bean zugegriffen werden. NetBeans bietet dabei Code Completion, d.h. es werden automatisch alle öffentlichen Methoden und Variablen der Managed Bean angeboten. Um die “message” der Managed Bean “HelloWorld” auszugeben, kann z.B. der Tag <h:outputText /> verwendet werden:

<h:outputText value="#{HelloWorld.message}" />

3.0 Deployment in Google App Engine
Sowohl das Lokale Deployment als auch das Remote Deployment erfolgt durch das Google App Engine Plugin komfortabel direkt aus NetBeans heraus.

3.1 Lokales Deployment in Google App Engine
Das lokale Deployment wird in NetBeans durch einen Rechtsklick auf das Projekt und die Auswahl von “Run” gestartet. Nach dem Deployment wird automatisch ein Browser mit der Webanwendung geöffnet.

Ausführen der JSF 2.0 Webanwendung auf dem lokalen Google App Engine Server
3.1 Remote Deployment in Google App Engine
Um die Anwendung in der tatsächlichen Google App Engine zu veröffentlichen, muss zunächst über die Weboberfläche der Google App Engine eine Anwendung mit eindeutigem Namen erstellt werden. Dieser Name muss mit dem Namen im “application” Tag das appengine-web.xml übereinstimmen. Das eigentliche Deployment erfolgt dann mit einem rechten Mausklick auf das Projekt und der Auswahl “Deploy To Google App Engine”.

Erstellen der Anwendung über die Google App Engine Weboberfläche
Damit wurde die JSF 2.0 Webanwendung erfolgreich in der Google App Engine veröffentlicht. :-)

Das komplette NetBeans Projekt inkl. WAR-Archiv kann hier heruntergeladen sowie unter http://1.latest.altes-kind.appspot.com/ direkt aufgerufen werden.

2 Kommentare

Skalierbarkeit von JEE in der Cloud

Hochschule FurtwangenIn den letzten Wochen habe ich mehr oder weniger intensiv versucht, ein für mich interessantes Thema für meine Master-Thesis im nächsten Semester zu finden bzw. konzipieren.

Nach Gesprächen mit verschiedenen Professoren habe ich mich nun für ein Thema mit dem vorläufigen Titel “Skalierbarkeit von JEE in der Cloud” entschieden, das ich bei Prof. Dr. Reich im Rahmen des Cloud Research Lab der Hochschule Furtwangen bearbeiten werde. Grundsätzlich soll die Arbeit die Skalierbarkeit von sowohl JEE Anwendungen (bzgl. EJBs, Transaktionen, Datenhaltung usw.) als auch JEE Applikationsservern (bzgl. dynamisches LoadBalancing, Konfiguration usw.) in Infrastructure as a Service (IaaS, z.B. Amazon EC2) und Platform as a Service (PaaS, z.B. Google AppEngine) Umgebungen untersuchen und somit auch “Best Practices” für JEE und Cloud Computing aufzeigen.

Wer sich nun für das Thema interessiert, findet einen guten Einstiegspunkt bzw. mehr Informationen in dem im Juli diesen Jahres veröffentlichtes Interview mit Adam Bien über JEE6 und Cloud Computing und in Zukunft sicher auch hier in meinem Blog (sofern meine Blogmotivation in Zukunft wieder etwas steigt). ;-)

Kein Kommentar

Polling in Prototype

Bisher hatte ich das JavaScript Framework Prototype nie direkt benutzt, sondern nur über andere Libraries wie script.aculo.us oder Lightbox 2.

Da ich meine Last.fm und Twitter Clients allerdings um ein wenig Ajax Funktionalität erweitern wollte, habe ich mir heute Prototype etwas genauer angeschaut – und bin schwer begeistert! Die Ajax-Update und Polling Funktionalität ist mit Prototype unglaublich einfach umzusetzen, wie folgendes Beispiel zeigt:

<div id="twitter">
  Laden...
</div>
<script type="text/javascript">
new Ajax.PeriodicalUpdater('twitter', 'twitter.php', {
  method: 'get', frequency: 60, decay: 1
});
</script>

In diesem Beispiel wird alle 60 Sekunden die twitter.php aufgerufen und der Response in den Div-Container mit der ID “twitter” geschrieben. Für meine Last.fm und Twitter Clients habe ich das bereits eingebaut – funktioniert super!

Mehr Informationen zum Ajax.PeriodicalUpdater von Prototype gibt es in den offiziellen API Docs.

Kein Kommentar

Sun Certified Web Component Developer

JavaNachdem ich vor etwa einem Jahr bereits die Sun Certified Programmer for the Java Platform, Standard Edition 5.0 – Zertifizierung gemacht habe, stand heute nun die Sun Certified Web Component Developer for the Java Platform, Enterprise Edition 5 – Zertifizierung auf dem Programm.

Obwohl die Prüfung viel schwieriger als erwartet war, habe ich mit 88% doch deutlich bestanden. Als Vorbereitungsbuch habe ich mit dem für die neue JEE5 Version der Zertifizierung zur Zeit einzigen auf dem Markt erhältlichen Buch Head First Servlets and JSP gearbeitet, was ich alles in allem auch empfehlen kann (auch wenn einige Themen zu kurz behandelt werden und das Final-Exam mehrere Fehler aufweist). Weiterhin sehr empfehlenswert ist die Linksammlung bei javaranch.com, hier sind mehrere kostenlose Mock-Exams sowie hilfreiche Zusammenstellungen und Tricks zu einzelnen Themengebieten zu finden.

Kein Kommentar