Autorenarchiv

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


“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


Ruby On Rails – Vorsicht bei der Spaltenbenennung!

Montag, 19. Mai 2008

Getreu den Grundprinzipien von Ruby On Rails „Don’t repeat yourself“ und „Convention over configuration“, versucht das Webframework dem Entwickler die Datenbankanbindung zu erleichtern. Hält man sich an die Konventionen, so genügt es zum Beispiel, die Spalten einer Tabelle nur in der Datenbank festzulegen. Zur Laufzeit stehen einem automatisch Zugriffsmethoden für die Spalten zur Verfügung. Diese werden einfach nach den Spaltennamen benannt. Es werden hierfür also keine Konfigurations- bzw. Mappingdateien mehr benötigt.

Problem

Allerdings führt dieser Mechanismus zu Einschränkungen bei der Auswahl der Spaltennamen. Tabellenspalten sollten nicht den selben Namen tragen wie Methoden der Klasse AciveRecord::Base oder der Klasse Object. Werden diese Einschränkungen nicht beachtet, resultiert dies unter Umständen in schwierig aufzufindenden Fehlern. Nennt man beispielsweise die Spalte einer Tabelle “class”, so wäre der konventionelle Zugriffsweg auf diesen Spaltenwert über eine Instanz t des Models dieser Tabelle: t.class

Da class aber eine Methode der Klasse Object (Basisklasse von ActiveRecord::Base) ist, erhält man mit t.class nicht den erwarteten Spaltenwert, sondern die Klasse der Instanzvariablen t. Benennt man eine Tabellenspalte nach einer Methode von ActiveRecord::Base, so wird also die Spaltenzugriffsmethode nicht zur Verfügung gestellt, da es ja bereits eine gleichnamige Methode gibt. Verwendet man nun diese entsprechend seiner Tabellenspalte benannte Methode, führt dies zu einem unerwartetem Programmverhalten.

Lösung

Es ist empfehlenswert, die erwähnten Einschränkungen bei der Spaltenbenennung zu beachten und damit die Probleme zu vermeiden. Möchte man dennoch eine seiner Spalten genau wie eine Methode von ActiveRecord::Base benennen, so kann man sich einfach einen eigenen getter schreiben:

    def get_class
 
    self.attributes["class"]
 
    end

Die Bezeichner, die man bei der Spaltenbenennung vermeiden sollte, können der Ruby- bzw. der Rails-API entnommen werden. (Members der Klassen Object und ActiveRecord::Base)


Christian Borkowski