Transaction Propagation – Ein Überblick

Nutzt man das Transaktionsmanagement in Spring, kann man als Entwickler über die Propagation einer Transaktion (Eigenschaft z.B. auf der Annotation) bestimmen, wie die aufgerufenen Methoden in Bezug auf die Transaktion gekapselt werden. Verschachtelt, oder in einer großen Transaktion, man hat hier viele Freiheiten. Ich möchte nun die verschiedenen Ausprägungen von Propagation vorstellen und dabei anmerken, wie sich diese bei einer Verschachtelung auswirken.

REQUIRED

Logik unter REQUIRED nutzt die selbe Transaktion wie eine aufrufende Logik, wenn diese bereits innerhalb einer Transaktion läuft. Wenn noch keine Transaktion vorhanden ist, wird eine neue gestartet. Als Beispiel nehmen wir die Logik OUTER und INNER, beide mit Propagation REQUIRED. OUTER und INNER sind gekoppelt, sprich OUTER nutzt die Funktionalität INNER. Der Aufruf von OUTER erzeugt also eine neue Transaktion, da noch keine vorhanden ist. Nun ruft OUTER die Logik von INNER auf. Es existiert bereits eine laufende Transaktion, daher nutzt INNER diese. Wird nun innerhalb von INNER ein Rollback verursacht, kann auch OUTER nicht mehr Commiten und unterliegt einem Rollback.

REQUIRES_NEW

Kennzeichnet Logik die immer innerhalb einer neuen Transaktion ausgeführt wird. Nehmen wir wieder an OUTER hat eine Transaktion und ruft INNER, welches die Propagation REQUIRES_NEW aufweist, auf, sind diese beiden Transaktionen distinkt. So in INNER ein Rollback erfolgt, bekommt die Transaktion OUTER davon nichts mit und umgekehrt. Während INNER ausgeführt wird ist die Transaktion von OUTER pausiert.

NESTED

Nested Logik nutzt die selbe Transaktion wie der Aufrufer, jedoch werden Savepoints genutzt, um die innere Transaktion von der Äußeren unabhängig(er) zu machen. In der inneren Transaktion kann so ein Rollback erfolgen bis zurück in die äußere Transaktion, ohne dass dieser Teil ebenfalls einem Rollback unterliegt.

MANDATORY

Kennzeichnet Logik, die eine bereits geöffnete Transaktion voraussetzt. Wird sie aufgerufen ohne einer laufenden Transaktion wird eine Exception vom Container geworfen.

NEVER

Beschreibt Logik, die in keiner Transaktion ausgeführt werden darf. Erfolgt dennoch ein Aufruf aus einer Transaktion heraus wird eine Exception vom Container geworfen.

NOT_SUPPORTED

Logik wird hierbei außerhalb der Transaktion aufgerufen. Ist eine Transaktion bereits vorhanden, wird sie für den Aufruf der Logik pausiert.

SUPPORTS

Wenn bereits eine offene Transaktion vorhanden ist, wird die Logik in dieser ausgeführt. Ist keine vorhanden, wird die Logik auch ohne Transaktion durchlaufen.

In diesem Sinne:

@Transactional(propagation=Propagation.REQUIRES_NEW)
public void haveSomeFun(User user) {
    // TODO have fun
}

Ähnliche Artikel:

Aus Zwei mach Eins

Da ich gerade meinen Heimserver um diverse Entwickler Tools aufstocken möchte (Jenkins, Sonar, Nexus) musste erstmal ein Servlet-Container her: Tomcat
Port 80 ist bereits freigegeben und eigentlich nervt mich das Portgetippe (eh schon wissen: localhost:8080) beim URL Aufruf ziemlich. Wär doch toll wenn man die Anwendungen die auf dem Tomcat laufen würden auch über den normalen 80er Zugriff erreichen könnte, oder?

Naja, dann ran ans Eingemachte!

Installation Tomcat

Java

So noch nicht geschehen sollte sinnvollerweise eine Java Installation am Server vorhanden sein

# apt-get install openjdk-6-jre

Wer auf dem Server auch Programme compiliern will besorgt sich ein jdk. Für die Installatin von Java 7 verweise ich auf einen ausgezeichneten Blog Beitrag von SysadminsLife

Download und Installation

Auf der Tomcat Seite die gewünschte Version (im Falle des Tutorials die neueste 7.0) mit wget herunterladen und entpacken

# tar xvfz apache-tomcat-7.0.35.tar.gz

danach an eine passende Stelle verschieben /opt/tomcat

# mv apache-tomcat-7.0.35 /opt/tomcat

Zum Abschluss noch die Scripts ausführbar machen

# chmod +x /opt/tomcat/bin/*.sh

Tomcat User und Gruppe

# groupadd tomcat
# useradd -g tomcat -d /opt/tomcat tomcat
# usermod -G www-data tomcat
# chown tomcat:tomcat /opt/tomcat -R

Tomcat Startup Script

Unter /etc/init.d/tomcat wird das Tomcat 7 Script von collinpeters abgespeichert (Oh ja, Github rulez! Und gist erst recht – Danke für das schöne Script!)

Natürlich muss das Script noch ausführbar gemacht werden und es soll automatisch starten

# chmod +x /etc/init.d/tomcat
# update-rc.d tomcat defaults

Tomcat Server User

Tomcat will auch abgesichert sein, zumindest ein bisschen. Daher legen wir einen Benutzer für die Admin und Manager Webapps an. Unter /opts/tomcat/conf/tomcat-users.xml ist folgendes zu ergänzen

<tomcat-users>
    <role rolename="manager" />
    <role rolename="manager-gui" />
    <role rolename="admin"/>
    <role rolename="admin-gui"   />
    <user username="USER" password="PASSWORT"
     roles="admin,admin-gui,manager,manager-gui" />
</tomcat-users>

Bitte USER und PASSWORT durch eure Werte ersetzen.

Start und Test

Es wär alles für die Katz würden wir es nicht auch testen. Dafür starten wir das Service

# /etc/init.d/tomcat start

Nun sollte Tomcat unter http://<server-ip>:8080 erreichbar sein.

Installation mod_jk Apache connector

apt-get

Wie immer, als erstes ist das Package zu installieren:

# apt-get install libapache2-mod-jk

Dieses Apache Modul dient als Brücke zwischen dem Webserver und Tomcat

mod_jk Konfiguration

Mit ein paar Zeilen unter /etc/apache2/conf.d/jk.conf wird mod_jk konfiguriert

<ifmodule mod_jk.c>
    JkWorkersFile /etc/apache2/workers.properties
    JkLogFile /var/log/apache2/mod_jk.log
    JkLogLevel error
</ifmodule>

mod_jk Worker

Apache benötigt nun noch eine Konfiguration in der festgehalten wird wie er einen Request auf Tomcat umleitet

Unter /etc/apache2/workers.properties legen wir daher folgende Konfiguratino ab

workers.tomcat_home=/opt/tomcat
workers.java_home=/usr/lib/jvm/java-6-openjdk
ps=/
worker.list=default
worker.default.port=8009
worker.default.host=localhost
worker.default.type=ajp13
worker.default.lbfactor=1

sites-available

Nun muss Apache nur noch erfahren welche Requests er an Tomcat weiterleiten soll. Dazu fügen wir in /etc/apache2/sites-available/default JkMount Zuordnungen ein. Im Falle der Tomcat Manager Applikation wären das folgende Zeilen 8 und 9

<VirtualHost *>
ServerAdmin root@localhost
ServerAlias homeserver

DocumentRoot /var/www/
# weitere Konfiguration

JkMount /manager default
JkMount /manager/* default

</VirtualHost>

Test

Nun muss Apache natürlich neu gestartet werden:

# /etc/init.d/tomcat restart

Das wars, nun sollte unter http://<server-ip>/manager die erste Weiterleitung auf den Tomcat sichtbar sein!

Viel Spaß damit!

Ähnliche Artikel:

Um auf eine Bean, die mit dem @Scope(“Session”) annotiert wurde, von einem nicht Session-Scoped Bean aus zugreifen zu können, ist ein aop-proxy notwendig.

Konnte man in der Spring XML Configuration noch bequem einen Proxy mit folgenden Zeilen definieren:

<!-- a HTTP Session-scoped bean exposed as a proxy -->
<bean id="userPreferences" class="com.foo.UserPreferences" scope="session">

     <!-- this next element effects the proxying of the surrounding bean -->
     <aop:scoped-proxy/>
</bean>

Hat man aber seine Bean wie oben beschrieben komplett über Annotationen konfiguriert, ist der Weg zum Scoped-Proxy ein nicht ganz so offensichtlicher. Man muss dazu die @Scope Annotation um den proxyMode erweitern:

@Scope( value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS )

Näheres findet ihr in der Spring API unter Scope

Ähnliche Artikel:

Veröffentlicht unter Spring.
Google App Engine

Tjo, mittlerweile ist der Hype um die Google App Engine schon wieder verflogen. Grund genug mich damit auseinanderzusetzen! Wenn das keinen Sinn ergibt ist das ok so, denn eigentlich spiele ich schon lange mit dem Gedanken mich mit der GAE zu beschäftigen. In Ermangelung des richtigen Projekts, zu vieler richtiger Projekte und zu vieler anderer Projekte, habe ich das aber immer wieder nach hinten verschoben. Bis heute! Denn ich brauche ein kleines Webinterface für eine Java Bibliothek die ich im Sinn habe. Und woher einen Servletcontainer nehmen wenn nicht stehlen? Nun z.B. über die Google App Engine.

Da ich auch meine Erfahrungen mit Spring weiter vertiefen möchte, bietet sich hierfür nun eine Kombination dieser beiden Technologien an. Damit das ganze dann auch noch hübsch aussieht möchte ich ich das ganze um eine gehobene JSF GUI Bibliothek erweitern: PrimeFaces. In dieser Riehe von Blogposts möchte ich nun dokumentieren welche Schritte notwendig waren um die berühmtesten Worte der Software-Development-Geschichte auf die Google App Engine unter Einsatz dieser Technologien zu zaubern: Hello World!

Und wo kämen wir hin wenn wir nicht die Abhängigkeiten sauber verwalten würden? Maven muss her! Als ran ans eingemachte: http://lmgtfy.com/?q=spring+maven+primefaces+google+app+engine

HALT!

Unsere BaugrubeWir wollen ja dabei eigentlich was lernen und nicht Fehler in irgendwelchen Blogeinträgen nachrennen, die die Hälfte von dem, was sie machen, auch erklären. (Daran sind frühere schnelle GAE Versuche mit PrimeFaces gescheitert, ich weiß also wovon ich spreche).

In der Hoffnung es mit meiner eigenen Blogreihe besser zu machen solls nun auch gleich losgehen.

Zuerst eine kurze Zusammenfassung der Zielsetzung

Eine Webanwendung unter Einsatz folgender Technologien:

  • Maven als Buildmanagement
  • Google App Engine als Servlet Container, Persistenz Schicht und schlicht zum Hosten
  • Spring als Mörtel zwischen meinen Java Klassen Ziegel
  • PrimeFaces als Verputz, damit das ganze am Schluss auch etwas gleichschaut.

Damit habe ich auch gleich eine gewisse Reihenfolge vorgegeben, wie Schritt für Schritt die Anwendung zusammengebastelt werden soll.

Als Erstes starten wir mit einem simplen Maven-GAE Projekt. Ich arbeite übrigends mit Maven 3.0.4 und der neuesten Version von Java 1.7

Damit sich Maven und die GAE gut vertragen nutzt man das maven-gae-plugin, auf Google Code bzw. Github, welches zum Zeitpunkt dieses Blogeintrags in der Version 0.9.4 vorliegt.

Damit erzeugen wir uns ein ganz frisches Maven Projekt:

mvn archetype:create\
-DarchetypeGroupId=net.kindleit\
-DarchetypeArtifactId=gae-archetype-jsf\
-DarchetypeVersion=0.9.4\
-DgroupId=your.groupId\
-DartifactId=your-artifactId

Danach haben wir ein gut dokumentiertes POM.xml erhalten, welches sich im Grunde selbst erklärt. Daumen hoch für die Entwickler!

Naja fast, es gibt da so ein paar Kleinigkeiten die fehlen, welche bekommen wir gleich angezeigt mittels

$ maven install

Es fehlt die GAE Versionsnummer
${gaeVersion}
sowie die Gae Plugin Versionsnummer
${gaePluginVersion}

Hätte man durchaus ins Template aufnehmen können mit einem “Huhu hier bearbeiten Kommentar!”, dem aufmerksamen Leser stach es beim Betrachten der POM aber bereits ins Auge.
Beide Einträge ersetzen wir durch die von uns genutzte Version, durch setzen eines entsprechenden Properties

Für die Properties gibt es bereits einen Eintrag
<gae.version>1.7.3</gae.version>

Für das Plugin schreiben wir selbst ein Property

<!--
Specify the Gae Maven Plugin Version
-->
<gaePluginVersion>0.9.4</gaePluginVersion>

 

Brick Wall in Philadelphia

Will man nun das Projekt erneut zum laufen kriegen bekommt man neben so einigen MegaByte App-Engine-Jar Files auch einen kleinen Stolperstein. (Im Gegensatz zu Version 0.9.2 hat das Plugin einiges dazugelernt und so sind einige Fehlerkorrekturen mit falschen Abhängigkeiten nicht mehr notwendig)

Wie gesagt ein Stolperstein ist noch vorhanden. Das maven-war-plugin meldet sich zu Wort, denn das Template hat zwar das POM erzeugt aber keinerlei Verzeichnisse

Diese sind flott nachgezogen, hier der Aufbau:
src
src/main
src/main/resources
src/main/webapp
src/main/webapp/WEB-INF
src/main/java
src/main/java/your.grouID.artefactId
src/test
src/test/resoruces
src/test/java
src/test/java/your.grouID.artefactId

Wir bekommen nun eindeutig Fahrwasser! Das nächste Problem dass uns ein

$ mvn install

offenbart ist eine fehlende web.xml! Und da drinnen wird schon die Anwendung konfiguriert. Das ist nun der Punkt an dem wir das erste Google App Engine Beispiel auf der Herstellerseite für Maven adaptieren.

Als erstes wird ein Servlet erstellt, dazu legen wir in unserem src/main/java ein entprechendes Java File an (Die Servletklasse des Tutorials)

Als nächstes ergänzen wir das Projekt um die web.xml und hinterlegen diese unter src/main/webapp/WEB-INF, ebenfalls wie im Tutorial

Abschließend noch die Konfigurationsdatei, wie im Tutorial, für die App Engine: appengine-web.xml welche wir ebenfalls unter src/main/webapp/WEB-INF ablegen

So frohlockend wie es nun voranging, so schnell laufen wir auch wieder auf Sand:

[ERROR] Failed to execute goal org.apache.maven.plugins:maven-war-plugin:2.1-bet
a-1:war (default-war) on project wls: Execution default-war of goal org.apache.m
aven.plugins:maven-war-plugin:2.1-beta-1:war failed: Cannot construct org.apache
.maven.plugin.war.util.WebappStructure as it does not have a no-args constructor
: Cannot construct org.apache.maven.plugin.war.util.WebappStructure as it does
not have a no-args constructor

Hier hat uns das das Alter des Templates einen Streich gespielt, da es noch nicht mit Java 7 umgehen kann. Erhöht man die Version des maven-war-plugin auf 2.1.1 hat man auch dieses Problem behoben.

Jetzt sollten wir in der Lage sein das Google App Engine Projekt zum laufen zu kriegen. Zuerst laden wir das SDK in unser Maven Repository

$ mvn gae:unpack

nun noch ein schnelles

mvn gae:run

und es sollte laufen.

Schön wär’s gewesen, würde alles funktionieren. Aber das Tutorial von Google hat in der appengine-web.xml scheinbar einen Punkt übersehen. So ist es wohl mittlerweile notwendig die Konfiguration für die Threadsicherheit zu hinterlegen. Wir holen dies mit

<threadsafe>false</threadsafe>

nach.

Nun nochmal

mvn gae:run

Und unter http://localhost:8080 steht auf einem Jetty Server unser GAE Hallo World bereit! Naja genauer unter http://localhost:8080/guestbook wenn ihr die GettingStarted Dateien eins zu eins übernommen habt.

Win Win Win!

Und im nächsten Blogeintrag werden wir das ganze um JSF erweitern!

Greets!

Ähnliche Artikel:

Veröffentlicht unter Java.

Eine leere Collection ist eine Collection die kein Element beinhaltet und nicht null. Warum tut man sich also immer wieder so etwas selbst an?

public List getOrders(String customerId){
    List<Order> result = null;

    try{
        result = OrderDAO.getOrders(customerId);
    } catch (InvalidAttributeValueException e){
        log.throwing(OrderDAO.class.getName(), "getOrders", e);
    }

    return result;
}

Eine Collection mit null zu initialisieren bedeutet nur das Problem an eine andere Stelle zu verschieben. Vielleicht nutzt ein Kollege die Methode ohne zu wissen, dass hier null statt einer leeren Collection zurückgegeben werden kann. Im schlimmsten Fall fällt es wärend dem Entwickeln nicht auf und irgendwann kracht es dann fröhlich beim Kunden mit einer NullpointerException.

Abhilfe schafft hier etwas Prävention mithilfe der Collections Klasse. Diese bietet für die drei Hauptgruppen von Collections leere Entsprechungen:

  • Collections.EMPTY_LIST;
  • Collections.EMPTY_SET;
  • Collections.EMPTY_MAP;

und bietet diese typsicher über eine Methode an:

  • Collections.emptyList();
  • Collections.emptySet();
  • Collections.emptyMap();

Und schon ist man auf der sicheren Seite und der Aufrufer braucht nur noch auf den Inhalt zu prüfen.

public List getOrders(String customerId){
    List<Order> result = Collections.emptyList();

    try{
        result = OrderDAO.getOrders(customerId);
    } catch (InvalidAttributeValueException e){
        log.throwing(OrderDAO.class.getName(), "getOrders", e);
    }

    return result;
}

Achja, Collections bietet noch weitere solcher Hilfsmethoden:

  • Collections.emptyListIterator();
  • Collections.emptyIterator();
  • Collections.emptyEnumeration();

So long, happy No-NullpointerException!

Ähnliche Artikel:

Veröffentlicht unter Java.