Techblog

Herzlich Willkommen im Communardo Techblog, dem Entwickler-Weblog von Communardo. An dieser Stelle werden Ideen, Problemlösungen, Tipps und Problemstellungen rund um die Entwicklung webbasierter Software vorgestellt.

techblog-teaser

Annotaion in Spring 3.0

, von

Annotations erfreuen sich immer größerer Beliebtheit. Egal wo man hinsieht, fast alles kann man mittlerweise damit machen (Caching, Persistenz, deklarative Services in OSGi, …). Seit Spring 3.0 gibt es nun auch im Springframework Annotations.

Ohne Annotations

Als Beispiel soll ein einfacher HalloWellt-Controller dienen. Klassen, die bei SpringMVC als Controller-Klassen fungieren, müssen das Interface Controller implementieren oder einfach von einer bestehenden Implementierungsklasse ableiten (z.B. SimpleFormController).

<bean id="urlMapping" class="org.springframework…SimpleUrlHandlerMapping">
  <property>
    <props>
      <prop key="/hallo">halloController</prop>
    </props>
  </property>
</bean>
<bean name="halloController" class="de.communardo.spring.mvc.HalloWeltController"/>

Über das URL-Mapping werden Conroller-Klassen unter einer URL registriert. Dieses wird durch das DispatcherServlet bei eingehenden Requests ausgewertet. In unserem Beispiel wird bei /hallo der HalloControlleraufgerufen.

Mit Annotations

Mit Spring 3.0 wird durch Einführung von Annotations vieles einfacher. Zum einen fällt das Implementieren oder Ableiten von Controller-Interfaces oder Klassen weg. Jede Klasse kann ein Controller sein. Entscheidend ist die Annotation @Controller.

package de.communardo.spring.mvc;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

<strong>@Controller</strong>
public class HalloWeltController {

  <strong>@RequestMapping</strong>(value = "/hallo")
  public <strong>@ResponseBody</strong> String helloWorld() {
    return "Hallo Welt";
  }
}

Über die Annotation @RequestMapping werden URL-Pfade auf Methoden gemappt. Über @ResponseBody wird der Rückgabewert String (="Hallo Welt") auf Response.out geschrieben.

Damit Spring die Klassen mit Annotation (er)kennt, müssen folgende 2 Zeilen in der Spring-XML-Konfiguration aufgenommen werden.

<mvc:annotation-driven />
<context:component-scan base-package= " de.communardo.spring.mvc" />

Dependency Injection

Eines der zentralen Themen bei Spring ist Dependency Injection – Abhängigkeiten zwischen Komponenten werden durch den Container per Setter injiziert. Bei Klassen, die per Spring-XML erzeugt werden, gibt es da eine spezielle Notation für das Injizieren von benötigten Komponenten.

<bean name="halloController">
  <property name="serviceLocater" ref="serviceLocater"/>
</bean>

<bean   class="de.communardo.spring.service.ServiceLocaterImpl"/>

Für Klassen, die per Annotation erzeugt werden, gibt es dafür entsprechende Annotation. Über die Annotation @Autowired werden per Setter eine Implementierung des Interfaces ServiceLocater injiziert.

package de.communardo.spring.mvc;

import org.springframework.beans.factory.annotation.Autowired;
import de.communardo.spring.service.ServiceLocater;

public class HalloWeltController {
  private ServiceLocater serviceLocater;

  <strong>@Autowired</strong>
  public void setServiceLocater(ServiceLocater serviceLocater) {
    this.serviceLocater = serviceLocater;
  }
}

FormController

Für das Formularhandling kommen weitere Annotations hinzu. @ModelAttribute definiert die Formbean, die an das Formular gebunden wird. Im Beispiel ist das userForm, die im Template per <form:form modelAttribute="userForm"> benutzt wird.

import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.bind.support.SessionStatus;

import de.communardo.spring.domain.User;
import de.communardo.spring.service.UserService;

<strong>@Controller</strong>
<strong>@RequestMapping</strong>("/editUser")
<strong>@SessionAttributes</strong>("userForm")
public class UserController {

  private static final String VIEW_TEMPLATE_USER = "user";
  private UserService userService;

  <strong>@RequestMapping</strong>(method = RequestMethod.POST)
  public String onSubmit(<strong>@ModelAttribute</strong>("userForm") UserForm userForm, BindingResult result, SessionStatus status) {

    new UserValidator().validate(userForm, result);
    if (result.hasErrors()) {
      return VIEW_TEMPLATE_USER;
    }
    userService.storeUser(new User(userForm.getFirstName(), userForm.getLastName(), userForm.getEmail()));
    return VIEW_TEMPLATE_USER;
  }

  <strong>@RequestMapping</strong>(method = RequestMethod.GET)
  public String setupForm(Model model) {
    UserForm userForm = new UserForm();
    model.addAttribute("userForm", userForm);
    return VIEW_TEMPLATE_USER;
  }

  <strong>@Autowired</strong>
  public void setUserService(UserService userService) {
    this.userService = userService;
  }
}

Ldap Schnittstelle mit Spring und iBatis

, von

Die Realisierung einer Schnittstelle zu einem Ldap Respository ist nach wie vor mit einem erheblichen Implementierungsaufwand verbunden und gestaltet sich oft als sehr unflexibel z.B. gegenüber Änderungen am Ldap Schema.

Beispiel für ein Ldap Schema und einen Ldap Eintrag:

Im Folgenden wird eine Lösung vorgestellt, wie sich mit Hilfe eines Jdbc-Ldap Treibers und einem Persistenzframework relativ schnell eine flexible Ldap Schnittstelle realisieren lässt.

Artikel vollständig lesen

Transactions mit dem Atlassian PluginFramework

, von
1 Kommentar

Auch bei der Pluginentwicklung für Confluence kommt irgendwann der Zeitpunkt, an dem man sich mit komplexeren Abläufen bei der Persistierung von Daten beschäftigen muss. Um die Integrität dieser Daten sicherstellen zu können, ist man dann auf den Einsatz von Transactions angewiesen. Da Confluence das Spring Framework verwendet, bietet sich hierfür die Nutzung des TransactionTemplate an. Dieses ermöglicht es, wie in der Spring Dokumentation beschrieben, auf einfache Weise kritischen Code unter Verwendung eines Callbacks in eine Transaction zu verpacken. Dazu benötigt man jedoch noch einen PlatformTransactionManager. Dieser implementiert die Strategie für die Transaktionsbehandlung, die im Fall von Confluence auf Hibernate basiert. Auch hierzu können weitere Informationen und ein Codebeispiel auf den Seiten von Spring gefunden werden. Der PlatformTransactionManager ist als Bean im Application Context von Confluence vorhanden und kann über den ComponentContainer referenziert werden:

public PlatformTransactionManager getTransactionManager(){
    return (PlatformTransactionManager) ComponentContainer.get("transactionManager");
}

Allerdings funktioniert dieses Codebeispiel nach einer Umstellung auf V2-Plugins nicht mehr. Der Grund: Da diese Plugins in OSGI-Bundles umgewandelt werden, welche eine andere Spring Version verwenden als die Kern-Applikation, kommt es bei der Referenzierung der transactionManager-Bean über den ComponentContainer zu einer ClassCastException.

Bei der Suche nach einer Lösung für dieses Problem bin ich über ein Ticket im Issue Tracker von Atlassian gestolpert, in dessen Kommentaren eine Alternative beschrieben wird: Abhilfe schafft die Verwendung der Shared Access Layer (SAL), einer einheitlichen Service-Schicht für alle Atlassian Anwendungen. Sie stellt neben verschieden anderen Services auch ein TransactionTemplate bereit, das für Confluence bereits den auf Hibernate basierenden PlatformTransactionManager gesetzt hat und auf folgende Weise referenziert werden kann.

Zunächst muss das Template als Komponente (Spring-Bean) im Plugin-Descriptor (atlassian-plugin.xml) importiert werden:

<component-import name="SAL Transaction Template" key="salTransactionTemplate">
    <interface>com.atlassian.sal.api.transaction.TransactionTemplate</interface>
</component-import>

Dann kann man sich die Komponente per Spring Autowiring in fast alle Plugin Modultypen (z. B. Actions und Components ) injizieren lassen. Der Name der zu injizierenden Bean entspricht dem Key des component-import Elementes im Plugin-Descriptor (in diesem Beispiel also “salTransactionTemplate” ):

public void setSalTransactionTemplate(TransactionTemplate template){
    this.transactionTemplate = template;
}

Nun kann das Template wie  oben beschrieben verwendet werden.

Noch ein Hinweis: SAL wird erst ab Confluence 3.0 mit ausgeliefert. Für Confluence 2.10 muss man also eine andere Lösung finden.

&lt;

Anbindung des OpenBenno Mailarchivs an ein Active Directory

, von
8 Kommentare

OpenBenno ist eine unter GPL lizenzierte E-Mail Archivierungslösung. Damit ist es möglich, die gesetzlichen Anforderungen zur Archivierung elektronischer Kommunikation umzusetzen. Soll diese Software im Unternehmen eingesetzt werden, ist zumeist eine Anbindung an ein vorhandenes Nutzerrepository via LDAP notwendig. Dieser Artikel beschreibt die notwendige Konfiguration der Anbindung an CAS und das Active Directory. Die zusätzlich notwendige Library, deren Sourcen und eine Beispielkonfiguration können am Ende des Artikels heruntergeladen werden.

Artikel vollständig lesen

OSGI- und Spring-DM-Bundles über Eclipse und maven2 erstellen

, von

Unter http://springosgi.googlepages.com/ befindet sich ein hervorragendes Tutorial zu Spring-DM, welches nebenbei auch die Verwendung der Plugin-Development-Unterstützung von Eclipse erklärt. Wer einmal deren Vorzüge genossen hat, wird wahrscheinlich nie mehr Manifest-Dateien per Texteditor bearbeiten wollen. Außerdem ist es bei der Entwicklung angenehmer, entwickelte Bundles über einen einzigen Knopfdruck im Eclipse zu starten, anstatt sie z.B. über Maven zu bauen und den Container extern zu starten.

Trotzdem kann es wichtig sein, dass die Bundles unabhängig von Eclipse in Maven gebaut werden können, z.B. um komplette Releases mit Tokenersetzung für verschiedene Umgebungen zu bauen. Für Maven2 existiert das Felix Bundle Plugin (http://felix.apache.org/site/apache-felix-maven-bundle-plugin-bnd.html), welches fertige Bundles generieren kann.

Da sich die Verszeichnisstrukturen des Eclipse-Plugin-Development-Tools und eines Standard-Maven2-Projektes sehr unterscheiden, wird hier ein Weg beschrieben, wie beide Welten vereinigt werden können.

Als Verzeichnisstruktur des Maven-Projektes wird folgendes vorausgesetzt:

- src

- main

- bundle

- META-INF

- Manifest.MF

- java

Innerhalb von Eclipse muss aber das META-INF-Verzeichnis im Wurzelverzeichnis des Projektes liegen, damit es als Bundle gebaut wird. Deshalb legen wir das Plugin-Projekt innerhalb des Bundle-Ordners an und importieren alle benötigten Resourcen (in diesem Fall nur den java-Ordner) über Verlinkung in das Projekt. Dafür erstellt man einen neuen Ordner und selektiert unter Advanced die Checkbox Link to Folder in file system. Dann wählt man den Java-Ordner und fügt ihn zum Projekt hinzu. Über das Kontext-Menü -> Build Path -> Use as source folder kann dieser Ordner  als Quellordner markiert werden.

Die Verzeichnisstruktur im Eclipse-Projekt sieht jetzt folgendermaßen aus:

- META-INF

- Manifest.MF

- java (Verlinkt)

Jetzt muss nur noch das Maven-Plugin so eingerichtet werden, daß es die Bundle-Konfiguration aus der vom Eclipse erzeugten Manifest-Datei übernimmt. Das kann z.B. so aussehen:

<plugin>

<groupId>org.apache.felix</groupId>

<artifactId>maven-bundle-plugin</artifactId>

<extensions>true</extensions>

<executions>

<execution>

<id>bundle</id>

<phase>package</phase>

<goals>

<goal>bundle</goal>

<goal>install</goal>

</goals>

</execution>

</executions>

<configuration>

<instructions>

<_failok>true</_failok>

<_include>src/main/bundle/META-INF/MANIFEST.MF</_include>

<Include-Resource>{maven-resources},META-INF/spring=src/main/bundle/META-INF/spring</Include-Resource>

</instructions>

</configuration>

</plugin>

Das _include -Tag bewirkt, dass für die Bundle-Generierung die von Eclipse erzeugte Manifest-Datei verwendet wird. Leider wird in Dieser der Klassenpfad Einträge enthalten, die im erzeugten Bundle nicht mehr vorhanden sind, z.B. der bin-Ordner, in welchem die kompilierten Klassen liegen. Das Bundle-Plugin bricht an dieser Stelle ab, obwohl ungültige Klassenpfad-Einträge in Equinox nicht zu Fehlern führen. Der einfachste Weg, dieses Problem zu umgehen, ist _failok auf true zu setzen. Eleganter wäre es, den Klassenpfad-Eintrag vorher zu manipulieren. Wichtig ist nur, dass der Klassenpfad-Eintrag ./ existiert, weil das Bundle-Plugin alle Klassen im Wurzel-Verzeichnis des Bundles ablegt.

Allerdings kann nicht davon ausgegangen werden, dass sich das Bundle auf Anhieb in einem extern gestarteten equinox genauso verhält, wie in Eclipse, da Eclipse zahlreiche Umgebungsvariablen setzt, die von den Default-Werten abweichen. Sollten also ClassNotFoundExceptions bei Klassen auftreten, welche normalerweise zur JRE gehören (z.B. org.w3c.dom.Node) und innerhalb von Eclipse nicht als Dependency eingebunden werden müssen, dann lohnt sich ein Blick in die System-Properties.

Properties prop = System.getProperties();
for (Object k : prop.keySet()) {
System.out.println(k + ” – ” + prop.getProperty(k.toString()));
}

Gibt alle Properties aus. Eine Beschreibung der einzelnen Parameter befindet sich hier

Wichtig sind vor allem die Parameter osgi.compatibility.bootdelegation und org.osgi.framework.system.packages.

Diese Webseite basiert auf Wordpress. © 2014 Communardo Software GmbH / Kleiststraße 10 a / D-01129 Dresden / Fon +49 (0) 351/8 33 82-0 / info@communardo.de