Herzlich Willkommen im Communardo Techblog, dem Entwickler-Weblog von Communardo. An dieser Stelle werden wir in loser Folge Ideen, Problemlösungen, Tipps und Problemstellungen rund um die Entwicklung webbasierter Software auf Basis verschiedenster Technologien vorstellen. Wir freuen uns über Rückmeldungen und Kommentare unserer Leser. Viel Spaß beim Lesen und Kommentieren.

21Jul

Wenn man in Spring-Anwendungen zum Beispiel den Aufruf von Methoden loggen will, aber nicht in jeder gewünschten Methode eine Log-Anweisung einfügen möchte, kann man das Logging zentral als Aspekt definieren. Es können damit die Methodenaufrufe aller Klassen geloggt werden, die als Bean durch Spring verwaltet werden.

Zu Beginn sollte die Bean für den Interceptor (advice) definiert werden.

<bean id="methodLoggingInterceptor" class="MethodLoggingInterceptor"/>

Danach kann eine advisor verwendet werden, der auf den Interceptor (advice) verweist und die joinpoints (die gewüschten Methoden) über einen regulären Ausdruck engrenzt.

<bean id="wsMethodLogger" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
     <property name="advice">
          <ref local="methodLoggingInterceptor"/>
     </property>
     <property name="patterns">
          <list>
               <value>de.communardo.*get.*</value>
               <value>de.communardo.*process.*</value>
          </list>
     </property>
</bean>

Dann muss den betreffenden Beans der neue Interceptor hinzugefügt werden. Hierbei ist auf die Reihenfolge zu achten.

<bean id="profileWebservice">
    ...
    <property name="interceptorNames">
         <list>
              <value>serviceTransactionInterceptor</value>
              <value>hibernateInterceptor</value>
              <value>wsMethodLogger</value>
         </list>
    </property>
</bean>

Die Interceptor-Klasse kann zum Beispiel die Interfaces MethodBeforeAdvice, AfterReturningAdvice und ThrowsAdvice implementieren. In den entsprechenden Methoden kann dann je nach Aufrufzeitpunkt eine geeignete Meldung ins Log geschrieben werden.

import java.lang.reflect.Method;
import org.apache.log4j.Logger;
import org.springframework.aop.AfterReturningAdvice;
import org.springframework.aop.MethodBeforeAdvice;
import org.springframework.aop.ThrowsAdvice;

public class MethodLoggingInterceptor implements MethodBeforeAdvice, AfterReturningAdvice,
ThrowsAdvice {
    private Logger log = null;
    public MethodLoggingInterceptor() {
    }
    public void before(Method method, Object[] args, Object object) throws Throwable {
        log = Logger.getLogger(object.getClass());
        log.debug("Beginning method: " + method.getName());
    }
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target)
    throws Throwable {
        log = Logger.getLogger(target.getClass());
        log.debug("Ending method: " + method.getName());
    }
    public void afterThrowing(Method method, Object[] args, Object target, Throwable ex) {
        log = Logger.getLogger(target.getClass());
        log.debug("Exception in method: " + method.getName() + " Exception is: " + ex.getMessage());
    }
}

Hinweis für AndroMDA-Nutzer: Für Klassen, die über den Stereotyp <<Service>> verfügen, existiert der tagged value @andromda.spring.service.interceptors. Darüber lassen sich den Services im Modell zusätzliche Interceptors hinzufügen, die in die Liste der interceptorNames aufgenommen werden. Die Spring-Konfiguration der beteiligten Beans kann anhand des applicationContext merge-point in den Application Context integriert werden.

Technorati Tags: , , , ,

14Jul

Auch die zweite Veranstaltung der Java User Group Saxony kann als voller Erfolg bezeichnet werden. Shaun Smith, Project Lead des EclipseLink Projektes und Produktmanager für Oracle TopLink, gab am 09.07.2008 einen Überblick über die Features und die Funktionsweise von EclipseLink.

Die Firma ubigrate hat diese Veranstaltung an der Informatik Fakultät der TU-Dresden in Zusammenarbeit mit dem JUG Saxony Team für die hiesige User Group organisiert und somit mehr als 50 Teilnehmern einen informativen und unterhaltsamen Abend ermöglicht.

Shaun Smith ging während seines Vortrages detailliert auf die Java Persistence API und die Referenzimplementierung EclipseLink ein. Dabei ist insbesondere die Vielseitigkeit des Frameworks hervorzuheben. Neben Objekt-Relationalem Mapping (ORM) stellt es Funktionen zum Objekt-XML Mapping (in Version 1.0 noch nicht 100% JAXB 2.0 kompatibel) sowie Unterstützung für Service Data Objects (SDO) 2.1 zur Verfügung. Sollte dies noch nicht genügen kann EclipseLink leicht um weitere Mapping Ziele erweitert werden wie Shaun Smith anhand eines JSON Beispiels demonstriert hat.

Neben der Vorstellung von technischen Details und der ein oder anderen Anekdote aus dem Projektgeschäft (wer sich dafür interessiert sollte die kommenden JUG Veranstaltungen nicht verpassen ;-) ) wurden auch die verschiedenen Mappings und das Zusammenspiel mit den Eclipse Dali JPA Tools im Rahmen von Live Demonstrationen vorgeführt.

Am Tag unserer Veranstaltung wurde EclipseLink 1.0 veröffentlicht und somit für den Produktiveinsatz freigegeben. Einen genaueren Blick ist es auf alle Fälle wert.

Das nächste Treffen der JUG Saxony ist für Oktober 2008 geplant. Das Thema lautet Rich Client Platform (RCP) Entwicklung mit Java.

Informationen zur kommenden Veranstaltung gibt es demnächst auf der Webseite der JUG Saxony unter http://www.jugsaxony.de. Die Folien zum EclipseLink Vortrag sind ebenfalls auf dieser Seite zu finden.

Einen Bericht zum Treffen in englischer Sprache gibt es im ubigrate Blog.

Technorati Tags: , , , , , ,

19Jun

… oder der lange Weg zum Glück

Manchmal hat man als Sharepoint-Entwickler das Bedürfnis, sich in einer benutzerdefinierten Liste an das Hinzufügen oder Ändern eines Items anzuhängen und die Aktion abzubrechen und dabei eine nette benutzerdefinierte Fehlermeldung ausgeben. Das sollte eigentlich überhaupt kein Problem sein, man erstellt einfach einen EventReceiver für ItemAdding bzw. ItemUpdating und setzt dort die ErrorMessage sowie die Cancel Property:

image

Nun erscheint allerdings leider nicht wie erwartet die benutzerdefinierte Fehlermeldung, sondern stattdessen eine böse Exception (DataFormWebPartException “The data source control failed to execute the insert command”):

clip_image002

Google verhilft uns zur Erkenntnis, dass dies ein Known Bug in WSS 3.0 ist (wird auch durch WSS 3.0 Service Pack 1 nicht behoben). Der Bug tritt offenbar nur in benutzerdefinierten Listen auf. Für dieses Problem ist ein Hotfix verfügbar. Der zugehörige Knowledge Base Artikel incl. Möglichkeit zum Anfordern des Hotfixes findet sich unter http://support.microsoft.com/default.aspx?scid=kb;en-us;949749

Aber Vorsicht! Nachdem man die Hürde genommen hat, die Mail mit dem Hotfix zu erhalten (Tipp: unter Junk E-Mail nachschauen) und frohgemut den Hotfix installiert hat (wobei man natürlich als gelernter Microsoft-Entwickler auch das iisreset nicht vergessen hat), geht hinterher auf dem Sharepoint Server erstmal gar nichts mehr:

image

Abhilfe schafft die Ausführung des Sharepoint Products and Technologies Configuration Wizard. Nach der Ausführung funktioniert nicht nur unser Sharepoint wieder, sondern nun wartet der EventReceiver auch mit unserer benutzerdefinierten Fehlermeldung auf:

image


19Jun

Um in Sharepoint anfragen auf eigene ASPX Seiten umzuleiten, besteht die Möglichkeit dies durch die Verwendung eines „IHttpModules“ zu realisieren. Das Modul greift spezifische Anfragen auf definierte Seiten ab und leitet diese auf definierte Seiten um.

Hier der Code, um eine Anfrage auf die CreateWebPage.aspx auf die MyPage.aspx umzuleiten:

pic1

Die Verwendung des IHttpModule, welches durch einen Eintrag in die „web.config“ realisiert wird, kann über ein Feature mit Hilfe des SPWebConfigModification Objektes erreicht werden. Diese Feature setzt und löscht automatisch den notwendigen Eintrag in der „web.config“.

Hier der Beispielcode um das oben gezeigte IHttpModule anzubinden:

pic2

pic3

Wichtig: Um auch die “Remove” Methode korrekt ausführen zu können, müssen die Eigenschaften des SPWebConfigModification Objektes richtig gewählt werden.

1. Die Eigenschaft “EnsureChildNote” muss angewandt werden, da mit der EnsureSection eine spätere “Remove” Methode keine Auswirkung liefert.

mod.Type = SPWebConfigModification.SPWebConfigModificationType.EnsureChildNode;

2. Der “mod.Name” muss entsprechend der “mod.Values” angepasst werden. Wird der Name des Objektes willkürlich gewählt, ist die Funktionalität der “Add” Methode gewährleistet, doch wird die Funktionalität der “Remove” Methode kein Resultat liefern.

entryvalue = @”<add name=”"IHttpModule”" type=”"namespace.class, assembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=…”" />”;

Der Angepasste “mod.Name” muss dann folgend lauten:

entryname = “add[@name='IHttpModule'][@type='namespace.class, assembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=...]“;

Hier nun noch der Inhalt der “feature.xml”. Zu beachten ist, dass der Scope auf der WebApplication liegen muss.

pic4

Links zum Thema:

http://blogs.technet.com/tatianasv/rss_tag_SharePoint+development.xml

http://msdn.microsoft.com/en-us/library/microsoft.sharepoint.administration.spwebconfigmodification.aspx

http://blog.tedpattison.net/Lists/Posts/Post.aspx?ID=4

MFG Gordon

Technorati Tags: ,

02Jun

Wer sich als Java-Entwickler bereits ein wenig mit der Sprache Groovy beschäftigt hat, wird während der Projektarbeit sicher schon einige Verwendungsmöglichkeiten für Groovy-Sprachkonstrukte gefunden haben. In den meisten Fällen betrifft dies eher relativ triviale Funktionen, wie Operationen im Dateisystem oder das Auswerten von Variablen eines Typs. Da aber gerade in der Erstellung von Prototypen die Evaluierung von Kernprozessen im Vordergrund steht, kann die ergänzende Verwendung von Skriptsprachen á la Groovy etwas mehr Geschwindigkeit und Flexibilität in die Entwicklung bringen.

Vorbereitungen

Um das eigene Projekt fit für den Groovy-Code zu machen, muss zunächst einmal die groovy-all-1.5.4.jar in den Build-Path aufgenommen werden. In der Regel findet sich diese Library im Verzeichnis der Groovy-Installation im Ordner \embeddable. Verwendet man Eclipse, so funktioniert das in etwa so ‘Project > Properties > Java Build Path > Libraries > Add External JARs…‘, wobei man es vorziehen sollte die Bibliothek in ein eigenes Projektverzeichnis zu kopieren (/lib) und von dort aus zu verlinken.

Theoretisch könnte es jetzt auch schon losgehen, aber ich denke die Wenigsten wollen beim Programmieren auf lieb gewonnene Features wie z.B Syntax Highlighting verzichten. Es gibt für die gängigsten IDE’s bereits Groovy-Plugins und ein paar Links dazu finden sich am Ende des Beitrages, wer mit Eclipse arbeitet kann mal einen Blick hierauf werfen. Die Plugins an sich sind zwar noch nicht 100% ausgereift und Autocompletion vermisst man stellenweise auch, aber hey - ich habe vor kurzem mit dem vi programmiert, auch das ging ;-)

Der erste Kontakt

Um einen schnellen Einstieg zu bekommen, soll ein kleines Beispielprojekt in Form eines Autohandels den Integrationsprozess veranschaulichen. Nachfolgend findet sich ein Listing zweier Interfaces die als Schnittstelle zur Groovy-Welt dienen.

package de.j2g.interfaces;

public interface ICar {
    public String getMake();
    public String getModel();
    public String getType();
    public double getPrice();
}

package de.j2g.interfaces;

import java.util.ArrayList;

public interface IGarage {
    public ArrayList<ICar> getCars();
}

Nachdem die Struktur steht, geht es an die Implementierung der Groovy-Klassen. In einem separaten Package wird die Klasse Car.groovy erzeugt, welche das Interface ICar implementiert:

Artikel vollständig lesen »

Technorati Tags: ,

21Mai

Um die Ladezeit einer Website bei reduzierter Bandbreite zu testen, können über das Freewaretool SHUNRA\Nimbus einfach und sehr schnell Bandbreiten zwischen 28.8 und 256 kbit/s emuliert werden.

Leider ist das Tool ein wenig veraltet und auf der offiziellen Website nicht mehr zum Download verfügbar, aber mit einer Google-Suche nach „shunra nimbus“ werden viele Quellen gefunden.

Es gibt ein weiteres, aktuelleres Freewaretool zum Drosseln der Bandbreite, das vor der ersten Nutzung aber einigen Konfigurationsaufwand erfordert (-> Details)

Ausgangspunkt meiner Recherche zu diesem Thema war der Versuch mit einem JMeter Lasttest Aufrufe von Webservices über Mobilfunk zu simulieren.

Mit SHUNRA\Nimbus hat das sehr gut funktioniert, da die Drosselung direkt für die Netzwerkkarte des PCs durchgeführt wird.

Technorati Tags: , ,

28Apr

 Nach dem Neuaufsetzen einer bestehenden Grails-Anwendung, tauchte nachfolgender Fehler während der Ansprache der Datenbank durch die Applikation auf:

Caused by: org.postgresql.util.PSQLException: ERROR: operator does not exist: bigint ~~ bigint
at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:1512)
at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:1297)
at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:188)
at org.postgresql.jdbc2.AbstractJdbc2Statement.execute(AbstractJdbc2Statement.java:437)
at org.postgresql.jdbc2.AbstractJdbc2Statement.executeWithFlags(AbstractJdbc2Statement.java:353)
at org.postgresql.jdbc2.AbstractJdbc2Statement.executeQuery(AbstractJdbc2Statement.java:257)
at org.apache.commons.dbcp.DelegatingPreparedStatement.executeQuery(DelegatingPreparedStatement.java:92)
... 2 more

Zunächst vermutete ich ein JDBC Treiber-Problem, jedoch konnte der Fehler nach etwas Recherche und Debugging auf das Object-Mapping eingegrenzt werden. Das Kernproblem ließ sich dann letzendlich seitens der Datenbank bzw. deren Ansprache durch das Grails ORM identifizieren - ich hatte versehentlich eine falsche Version der PostgreSQL Datenbank installiert. Da Grails Hibernate als Persistenz-Framework verwendet (GORM) und  dieses nur Postgres bis zur Version 8.1 unterstützt,  quittierte meine Application die meisten Datenbankoperationen mit obiger Meldung.

Links: Von Hibernate unterstützte Datenbanken

 

Technorati Tags: , ,

21Apr

Gelegentlich werden in Spalten von ListItems auch Benutzerinformationen gespeichert - als Beispiel sollen hier “erstellt von” und “geändert von” dienen. Die zugehörigen Felder des Items beinhalten meist Zeichenketten im Format <BenutzerID>;#<Benutzername>.

Bevor man sich nun in String-Operationen versucht - z.B. durch Split an der Stelle “;#” und Erzeugung der Objekte über die extrahierte ID - sollte man sich mit den Klassen SPFieldUser bzw. SPFieldUserValue auseinandersetzen, die hier viel Arbeit und Fehleranfälligkeit abnehmen können, da hier sehr einfach Objekte vom Typ SPUser bzw. SPGroup zurückgegeben werden können.

Im Folgenden 2 beispielhafte Methoden, die den Zugriff verdeutlichen sollen:

private SPUser GetSPUser(SPListItem item, Guid fieldid)
{
    SPUser retval = null; 

    #region check params
    if (item == null)
    {
        throw new ArgumentNullException("item");
    }
    if (fieldid == Guid.Empty)
    {
        throw new ArgumentException("Empty Guid is not allowed.", "fieldid");
    }
    #endregion 

    try
    {
        SPFieldUser field = item.Fields[fieldid] as SPFieldUser;
        if (field != null)
        {
            SPFieldUserValue fieldValue = field.GetFieldValue(item[fieldid].ToString()) as SPFieldUserValue;
            if (fieldValue != null)
            {
                retval = fieldValue.User;
            }
        }
    }
    catch (Exception ex)
    {
        throw ex;
    } 

    return retval;
}
private SPGroup GetSPGroup(SPListItem item, Guid fieldid)
{
    SPGroup retval = null; 

    #region check params
    if (item == null)
    {
        throw new ArgumentNullException("item");
    }
    if (fieldid == Guid.Empty)
    {
        throw new ArgumentException("Empty Guid is not allowed.", "fieldid");
    }
    #endregion 

    try
    {
        SPFieldUser field = item.Fields[fieldid] as SPFieldUser;
        if (field != null)
        {
            SPFieldUserValue fieldValue = field.GetFieldValue(item[fieldid].ToString()) as SPFieldUserValue;
            if (fieldValue != null)
            {
                string groupName = fieldValue.LookupValue;
                retval = item.Web.SiteGroups[groupName];
            }
        }
    }
    catch (Exception ex)
    {
        throw ex;
    } 

    return retval;
}

Der Aufruf ist nun recht trivial:

SPUser modifiedbyuser = GetSPUser(tasklistitem, SPBuiltInFieldId.Editor);
DateTime modifiedat = Convert.ToDateTime(tasklistitem[SPBuiltInFieldId.Modified]);
SPUser assignedto = GetSPUser(tasklistitem, SPBuiltInFieldId.AssignedTo);
Technorati Tags: , ,

17Apr

Bei der Entwicklung komplexer Workflows für den SharePoint spielen auch Workflowaufgaben für die Benutzerinteraktion während der Ausführung des Workflows eine nicht unbedeutende Rolle.

Eine solche Aufgabe hat nicht nur die Standardeigenschaften wie einen Titel oder eine Beschreibung sondern auch erweiterte Eigenschaften. Das praktische daran ist, dass diese kein eigenes Feld in der Liste benötigen sondern die Daten in den bereits vorhandenen Feldern gespeichert werden.

Und wenn man weiß, wie es geht, ist der Zugriff auf diese Daten auch sehr einfach.

1.) bei der Erstellung
Während der Erstellung der Aufgaben verwendet man in der Regel den Typ WssTaskActivity. Dieser verfügt bereits über eine Eigenschaft ExtendedProperties, die vom Typ Hashtable ist und in die man seine gewünschten Daten schreiben kann.

createTask.TaskId = Guid.NewGuid();
createTask.TaskProperties.Title = "Bitte genehmigen";
createTask.TaskProperties.ExtendedProperties["AdditionalText"]
    = "Bitte persönlich bearbeiten!";

2.) über den Item der TaskList

Möchte man nun später noch einmal darauf zugreifen, sieht man sich vor die Aufgaben gestellt, die Daten wieder aus dem SPListItem der TaskList zu lesen und nach der Änderung auch wieder zu schreiben. Sieht man sich das SPListItem genauer an, das die Aufgabe repräsentiert, so erkennt man, dass es ein Feld ExtendedProperties enthält, in dem die Daten auch enthalten sind - allerdings sind hier einfach die XML-Attribute die die Eigenschaften repräsentieren angegeben:

"AdditionalText='Bitte persönlich bearbeiten!' Department='Einkauf'"

Die große Frage ist jetzt, wie man einfach auf diese Daten zugreift, diese verändert und wieder abspeichert. Die Hashtable die bei der Erstellung wäre hierfür ein adäquates Objekt, das hier aber wie es zunächst scheint nicht vorhanden ist. Aber man muss hier nicht verzagen - auch dafür gibt es eine bereits vorhandene Lösung, auf die man aber erst kommen muss. Der Typ SPWorkflowTask aus dem NameSpace Microsoft.SharePoint.Workflow verfügt über 2 interessante statische Methoden: GetExtendedPropertiesAsHashtable hat als Rückgabewert genau die eben noch vermisste Hashtable und AlterTask verfügt über die Möglichkeiten, den Item mit den geänderten Daten auch wieder zu aktualisieren.

// get the extended properties hashtable
Hashtable taskItemExtProps = SPWorkflowTask.GetExtendedPropertiesAsHashtable(this.TaskListItem);
// write new values to the hashtable
taskItemExtProps["AdditionalText"] = "Mein Kommentar: genehmigt!";
// update the task item with new values
SPWorkflowTask.AlterTask(this.TaskListItem, taskItemExtProps, true);
Technorati Tags: , , , , ,

11Apr

Am 03. April 2008 fand das erste Treffen und damit die Gründungsveranstaltung der Java User Group Saxony in den Räumen der Communardo Software GmbH statt. Diese Gruppe wurde auf Initiative von Torsten Rentsch (Communardo Software GmbH) und Falk Hartmann (ubigrate) Anfang 2008 ins Leben gerufen. Ziel ist es den Wissensaustausch im Java Umfeld zu fördern sowie Kontakte zwischen Firmen und wissenschaftlichen Einrichtungen zu knüpfen.

JUG Saxony Gründungsveranstaltung

Vor 43 interessierten Teilnehmern wurde die Model Driven Architecture (MDA) vorgestellt. Herr Professor Aßmann (Lehrstuhl Softwaretechnik der TU Dresden) gab eine konzeptionelle Einleitung zum Thema und betrachtete dabei insbesondere den Zusammenhang zwischen MDA und Component-based Software Engineering (CBSE) (Folien). Herr Torsten Lunze (Communardo Software GmbH) stellte mit seinem Vortrag (Folien) den Bezug zur Praxis anhand eines Beispiels aus dem Projektgeschäft her und erläuterte u.a. die Vor- und Nachteile von MDA beim Einsatz der Open Source Software AndroMDA. Beim anschließenden Buffet blieb Zeit Kontakte zu knüpfen und das Thema im Gespräch zu vertiefen.
Mindestens einmal pro Quartal wird nun eine Veranstaltung der JUG Saxony stattfinden. Das nächste Treffen ist am 9. Juli 2008 in den Räumen der TU Dresden geplant. Das Thema wird voraussichtlich EclipseLink (JPA 2.0 RI, JSR 317) sein.
Gern können Sie sich als Mitglied der JUG Saxony auf unserer Website http://groups.google.de/group/jug-saxony registrieren. Für Anregungen und Wünsche sind wir stets offen.

Technorati Tags: , , , ,