Mit ‘Spring’ getaggte Artikel

Transaktionssteuerung mit Spring AOP

Freitag, 24. April 2009

Spring bietet uns mit AOP die Möglichkeit innerhalb der Spring Konfiguration die Behandlung der Datenbanktransaktionen zu steuern. Hierfür benötigen wir lediglich eine AOP Konfiguration, die auf alle Methoden zeigt, die innerhalb einer Transaktion ablaufen sollen und den Spring Tag .

In diesem Beispiel gehen wir von zwei Service Klassen aus, deren Methoden von einer Transaktion umschlossen werden sollen.

applicationContext.xml:

<-- service beans -->
<bean id="fooService" class="a.b.service.FooService" />
<bean id="barService" class="a.b.service.BarService" />
 
<-- hibernate transaction manager -->
<bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    <property name="sessionFactory" ref="sessionFactory" />
</bean>
 
<-- hibernate session factory -->
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
    <property name="dataSource">
        <ref bean="dataSource" />
    </property>
    <property name="annotatedClasses">
        <list>....</list>
    </property>
    <property name="hibernateProperties">
        <props>
            <prop key="hibernate.dialect">org.hibernate.dialect.PostgreSQLDialect</prop>
        </props>
    </property>
</bean>

Innerhalb des tx:advice definieren wir das Verhalten für die einzelnen Methoden der Services. Für alle Methodenaufrufe werden die gleichen Isolation- und Propagation-Level benutzt. Für Methoden die mit „find“ oder „get“ beginnen setzen wir die Transaktion noch auf read-only. In diesem Methoden dürfen jetzt nur noch Datenbank SELECTs ausgeführt werden, anderenfalls würde es eine Exception geben. Die Datenbank kann so das Locking auf Zeilen, die von diesem Methodenaufrufen betroffen, sind optimieren.

applicationContext.xml:

<tx:advice id="defaultTranscationAdvice" transaction-manager="txManager">
    <tx:attributes>
        <tx:method name="*" isolation="READ_COMMITTED" propagation="REQUIRED" />
        <tx:method name="find*" read-only="true" />
        <tx:method name="get*" read-only="true" />
    </tx:attributes>
</tx:advice>

Als letztes brauchen wir jetzt noch die AOP Konfiguration, die das defaultTransactionAdvice mit den Service Klassen verbindet. Durch die Definition „a.b.service.*.*(..)“ wird um jede Methode in jeder Klasse innerhalb des Paketes a.b.service eine Transaktion gelegt. Es können so weitere Service Klassen hinzugefügt werden, ohne dass das Transaktion Handling angepasst werden muss.

applicationContext.xml:

<aop:config>
    <aop:pointcut id="allServiceMethods" expression="execution(* a.b.service.*.*(..))" />
    <aop:advisor advice-ref="defaultTranscationAdvice" pointcut-ref="allServiceMethods" />
</aop:config>

Innerhalb der Service Methoden brauchen wir uns nun keine Gedanken mehr um offene oder geschlossene Transaktionen machen. Wir bewegen uns in jedem Fall innerhalb einer Transaktion. Sollte es zu einem Fehler kommen, so wird ein Rollback auf allen Änderungen der Methode durchgeführt.
Erst beim Verlassen der Service Methode müssen wir berücksichtigen, dass Objekte ggf. nicht mehr an eine Session gebunden sind (hier kann es dann zu Problemen mit LazyLoading o.ä. kommen).


Felix Breske


Spring Singleton

Donnerstag, 20. November 2008

Wer in Spring-Konfigurationen seiner Anwendung Beans als Singletons definiert, sollte sich darüber im Klaren sein, dass er mit diesem Bean-Scope keine Umsetzung des Singleton-Entwurfsmusters der Gang of Four erreicht.
Eine Spring Bean, die mit dem Singleton-Scope versehen wurde (default-Konfiguration), ist lediglich im Kontext einer IOC-Container-Instanz einzigartig. D.h. dass es durchaus mehrere Instanzen einer solchen Singleton Bean geben kann. Nämlich genau dann, wenn man sich mehrere IOC-Container-Instanzen in Form von ApplicationContext-Instanzen erzeugt. Jede dieser Instanzen verwaltet dann ihre eigenen Beans.
Ein Verständnis dieses Konzepts hilft dabei, schwierig aufzufindende Fehler zu vermeiden.
Möchte man zum Beispiel in seiner Anwendung an irgendeiner Stelle Zugriff auf eine Spring Bean haben und diesen nicht per Dependency Injection ermöglichen, so muss man sich den Zugriff mit Hilfe einer ApplicationContext-Instanz verschaffen. Erstellt man sich nun aber diese Instanz zum Beispiel mittels

new ClassPathXmlApplicationContext(path);

dann muss man beachten, dass damit eine neue IOC-Container-Instanz erzeugt wird, deren Singleton Beans nicht die sind, die von der Anwendung für die Dependency Injection verwendet werden. Denn hierfür wird eine IOC-Instanz benutzt, die beim Anwendungsstart generiert wird.
Definiert man nun zum Beispiel Beans, die eine Datenbankverbindung erzeugen, so führt die gerade beschriebene Art und Weise der Zugriffsverschaffung auf diese Beans dazu, dass mit jeder ApplicationContext-Instanz auch jeweils neue Verbindungen geöffnet werden, bis es irgendwann zu viele für den Datenbank Server sind.

Es ist ratsam, Spring Bean Instanzen nur per Dependency Injection zur Verfügung zu stellen. Falls dies nicht möglich ist, so sollte man sich bemühen, die in seiner Anwendung bereits automatisch erzeugte und benutzte ApplicationContext-Instanz zu verwenden. Dies ist bei Web-Anwendungen zum Beispiel möglich durch:

ContextLoader.getCurrentWebApplicationContext();

Christian Borkowski


Rest Services mit Spring und Restlet

Montag, 10. November 2008

Mit der Version 1.1 (aktuell RC2) bekommt das Restlet Framework eine Integration für Spring. Die Konfiguration erfolgt über Web- und Spring-Context der Anwendung. Hierfür stehen dem Entwickler die Klassen RestletFrameworkServlet und SpringRouter zur Verfügung.

Im Gegensatz zu Restlet 1.0 braucht der “Restlet Router” und die “Restlet Application” nicht mehr von dem Entwickler implementiert werden. Nach der Konfiguration in der web.xml und applicationContext.xml müssen nur noch die Anwendungsspezifischen Resources selbst umgesetzt werden. Das Mapping der Resource-URL auf die Restlets erfolgt dabei über die applicationContext.xml.

RestletFrameworkServlet
Das RestletFrameworkServlet stellt die Verbindung zwischen Web Context und Spring Konfiguration für Restlet da. Es nimmt die HTTP Anfragen entgegen und leitet sie an den konfigurierten Router um.

web.xml

<!-- Restlet -->
<servlet>
	<servlet-name>restlet</servlet-name>
	<servlet-class>com.noelios.restlet.ext.spring.RestletFrameworkServlet</servlet-class>
	<init-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>/WEB-INF/applicationContext.xml</param-value>
	</init-param>
	<init-param>
		<param-name>targetRestletBeanName</param-name>
		<param-value>springRouter</param-value>
	</init-param>
	<load-on-startup>1</load-on-startup>
</servlet>
 
<servlet-mapping>
	<servlet-name>restlet</servlet-name>
	<url-pattern>/*</url-pattern>
</servlet-mapping>

SpringRouter
Der Spring Router verteilt die eingehenden Anfragen an die konfigurierten Rest Resources. Im Spring Context wird dabei ein Mapping von URLs auf Restlet Beans vorgenommen. Also Beispiel wird hier eine Klasse Note verwendet die eine Id und einen Text beinhaltet.

applicationContext.xml

 
<!-- restlet -->
<bean id="springRouter" class="org.restlet.ext.spring.SpringRouter">
	<property name="attachments">
		<map>
			<entry key="/notes/{id}">
				<bean class="org.restlet.ext.spring.SpringFinder">
					<lookup-method name="createResource" bean="noteResource" />
				</bean>
			</entry>
			<entry key="/notes">
				<bean class="org.restlet.ext.spring.SpringFinder">
					<lookup-method name="createResource" bean="notesResource" />
				</bean>
			</entry>
		</map>
	</property>
</bean>
 
<!-- restlet resources -->
<bean id="notesResource" name="notesResource" class="org.example.NotesResource" scope="prototype">
	<property name="noteService" ref="noteService" />
</bean>
 
<bean id="noteResource" name="noteResource" class="org.example.NoteResource" scope="prototype">
	<property name="noteService" ref="noteService" />
</bean>

Jetzt fehlen nur noch die Implementierung der Resource Beans. Hier muss lediglich beachtet werden, dass die Resources einen parameterlosen Konstruktor haben und die Context spezifischen Parameter über die init() Methode übergeben werden.

NoteResource.java

public class NoteResource extends Resource {
	private Note note;
	private NoteDAO noteDAO;
 
	public NoteResource() {
	}
 
	public void setNoteDAO(final NoteDAO noteDAO) {
		this.noteDAO = noteDAO;
	}
 
	@Override
	public void init(final Context context, final Request request, final Response response) {
		super.init(context, request, response);
		final Integer noteId = Integer.parseInt((String) getRequest().getAttributes().get("id"));
		note = noteDAO.findById(noteId);
		if (note != null) {
			getVariants().add(new Variant(MediaType.TEXT_XML));
		}
	}
 
	@Override
	public Representation getRepresentation(final Variant variant) {
		if (MediaType.TEXT_XML.equals(variant.getMediaType())) {
			try {
				final DomRepresentation representation = new DomRepresentation(MediaType.TEXT_XML);
				final Document document = representation.getDocument();
 
				final Element eltItem = document.createElement("note");
				document.appendChild(eltItem);
				final Element idElement = document.createElement("id");
				idElement.appendChild(document.createTextNode(Integer.toString(note.getId())));
				eltItem.appendChild(idElement);
 
				final Element textElement = document.createElement("text");
				textElement.appendChild(document.createTextNode(note.getText()));
				eltItem.appendChild(textElement);
 
				document.normalizeDocument();
 
				return representation;
			} catch (final IOException e) {
				e.printStackTrace();
			}
		}
		return null;
	}
}

NotesResource.java

public class NotesResource extends Resource {
	private List<Note> notes;
	private NoteDAO noteDAO;
 
	public NotesResource() {
	}
 
	public void setNoteDAO(final NoteDAO noteDAO) {
		this.noteDAO = noteDAO;
	}
 
	@Override
	public void init(final Context context, final Request request, final Response response) {
		super.init(context, request, response);
		notes = noteDAO.getAllNotes();
		getVariants().add(new Variant(MediaType.TEXT_XML));
	}
 
	@Override
	public Representation getRepresentation(final Variant variant) {
		if (MediaType.TEXT_XML.equals(variant.getMediaType())) {
			try {
				final DomRepresentation representation = new DomRepresentation(MediaType.TEXT_XML);
				final Document document = representation.getDocument();
				final Element root = document.createElement("notes");
				d.appendChild(root);
				for (final Note note : notes) {
					final Element eltItem = document.createElement("note");
					final Element idElement = document.createElement("id");
					idElement.appendChild(document.createTextNode(Integer.toString(note.getId())));
					eltItem.appendChild(idElement);
 
					final Element textElement = document.createElement("text");
					textElement.appendChild(document.createTextNode(note.getText()));
					eltItem.appendChild(textElement);
 
					root.appendChild(eltItem);
				}
				document.normalizeDocument();
				return representation;
			} catch (final IOException e) {
				e.printStackTrace();
			}
		}
		return null;
	}
}

Wird nun die URL /notes (innerhalb der Webanwendung) aufgerufen wird eine Liste aller Einträge angezeigt; mit /notes/0 kann dann direkt auf einen Eintrag zugegriffen werden.

Für schreibende Zugriffe auf die Resources lohnt sich ein Blick in Dokumentation der Resource Klasse und in das Restlet Tutorial auf restlet.org.


Felix Breske


“Code First, Contract Last” Soap Web Services mit Spring

Donnerstag, 14. August 2008

Möchte man einen Web Service veröffentlichen, der von Springs Dependency Injection Mechanismus Gebrauch macht, so muss der Web Service auf den Spring Application Context zugreifen können.

Spring liefert ein eigenes Web Services Framework “Spring-WS” mit. Allerdings ist dieses Framework darauf ausgerichtet, dass die Web Services dem “Contract First, Code Last” – Ansatz nach entwickelt werden. D.h. man schreibt zunächst das XML Schema und das WSDL-Dokument, welche den Web Service definieren und implementiert anschließend, den aufgestellten Vorschriften entsprechend, die Service-Klassen. Für diesen Weg bietet Spring-WS einige Unterstützung. So kann man sich z.B. aus einem definierten XML-Schema eine WSDL generieren lassen.

Hat man aber bereits die Service-Klassen implementiert und möchte deren Funktionalität nun als Web Services veröffentlichen, so bieten sich andere Hilfsmittel dafür an. Im Folgenden wird beschrieben, wie man mit XFire und JSR 181 Annotations den “Code First, Contract Last” – Ansatz zur Entwicklung von Spring Web Services verfolgen kann. Dazu wird ein einfacher “Hello World” – Web Service erstellt, der von Spring verwaltete Beans verwendet.

Zunächst schreibt man sich eine Java-Klasse, die die helloWorld-Methode implementiert.

package de.baderjene.backend.webServiceAPI;
 
import org.springframework.stereotype.Component;
 
@Component("helloWorldCore")
public class HelloWorldCore {
 
  public String helloWorld() {
    return "hello world!";
  }
}

Anschließend implementiert man die annotierte Web Service Klasse, die die Klasse HelloWorldCore per Dependency Injection erhält.

package de.baderjene.backend.webServiceAPI;
 
import javax.jws.WebMethod;
import javax.jws.WebResult;
import javax.jws.WebService;
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
 
@WebService(name = "ExampleWebService", targetNamespace = "http://pegasus/services/ExampleWebService")
@Component("exampleWebService")
public class ExampleWebService {
 
  @Autowired
  HelloWorldCore helloWorldCore;
 
  @WebMethod(operationName = "helloWorld", action = "urn:helloWorld")
  @WebResult(name = "result")
  public String helloWorld() {
    return helloWorldCore.helloWorld();
  }
}

Nun schreibt man noch die benötigten Konfigurationsdateien:
1. Spring Application Context

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:context="http://www.springframework.org/schema/context"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
                               http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
                               http://www.springframework.org/schema/context
                               http://www.springframework.org/schema/context/spring-context-2.5.xsd">
 
 <context:component-scan base-package="de.baderjene.backend"/>
 
<context:annotation-config/>
 
</beans>

2. xfire-servlet.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
    <import resource="backendContext.xml"/>
    <bean id="webAnnotations"
        class="org.codehaus.xfire.annotations.jsr181.Jsr181WebAnnotations" />
    <bean id="jsr181HandlerMapping"
        class="org.codehaus.xfire.spring.remoting.Jsr181HandlerMapping">
        <property name="typeMappingRegistry">
            <ref bean="xfire.typeMappingRegistry" />
        </property>
        <property name="xfire" ref="xfire" />
        <property name="webAnnotations" ref="webAnnotations" />
    </bean>
</beans>

3. web.xml

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
    PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
    "http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            classpath:org/codehaus/xfire/spring/xfire.xml
            /WEB-INF/xfire-servlet.xml
        </param-value>
    </context-param>
 
   <listener>
        <listener-class>
            org.springframework.web.context.ContextLoaderListener
       </listener-class>
    </listener>
 
    <servlet>
        <servlet-name>XFireServlet</servlet-name>
        <servlet-class>
            org.codehaus.xfire.spring.XFireSpringServlet
        </servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>XFireServlet</servlet-name>
        <url-pattern>/servlet/XFireServlet/*</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>XFireServle</servlet-name>
        <url-pattern>/services/*</url-pattern>
    </servlet-mapping>
</web-app>

Zum Ausprobieren kann man nun ein War-Packet erstellen, dieses z.B. in das webapps-Verzeichnis vom Tomcat legen und den Tomcat starten.


Christian Borkowski