Montag, 3. Januar 2011

AOP Advice für den EnterpriseLib 5.0 Validation Block

Das Ziel ist es, ein Domänenmodell an einer Schnittstelle zu validieren, ohne die vorhandene Schnittstellenimplementierung zu ändern. Um dieses zu bewerkstelligen, möchte ich mit Hilfe von Spring.Net und der Enterprise Library einen aspektorientierten Ansatz verfolgen.

Im vorliegenden Fall wurden Validierungsregeln mit Hilfe von ValidationAttributes aus dem Validation Block der Enterprise Library 5.0 erstellt. In diesem Framework gibt es jedoch auch die Möglichkeit Validationsregeln anstelle von Attributen per Code oder Konfiguration zu hinterlegen.

Eine fast fertige Lösung um einen AOP-Advice um eine Schnittstelle zu legen und diese automatisch alle übergebenen Argumente überprüfen zu lassen, befindet sich bereits im Namensraum Microsoft.Practices.EnterpriseLibrary.Validation.PolicyInjection der EnterpriseLibrary. Jedoch sind die dort vorhandenen Klassen zum einen ausgelegt für Microsofts Inversion of Control (IoC) Container Unity, zum anderen werden an eine Methode übergebene Parameter zwar überprüft, schlägt eine Überprüfung jedoch fehl, werden weitere Parameter nicht mehr überprüft.

Grundsätzlich sind ist die Vorgehensweise bei den IoC-Containern Unit und Spring ähnlich: die es werden Einstiegspunkte (Pointcuts) definiert, bei deren Durchlauf weitere Funktionalitäten (Aspect) ausgeführt werden. Erstellt der Container eine Instance eines Objektes, wird entsprechend der Konfiguration ein Proxy-Objekt erzeugt, welches die tatsächliche Objekt-Instanz kapselt und so in der Lage ist Aufrufe abzufangen und durch entsprechend konfigurierte Aspekte zu leiten. Dieser Ansatz wird als “Runtime Weaving” bezeichnet, da Aspekte zur Programmlaufzeit hinzugefügt/entfernt werden. Weitere Möglichkeiten anderer Frameworks (wie beispielsweise PostSharp)  sind das Einmischen von ByteCode bzw. IL-Anweisungen.

Als simplifiziertes Domänenmodell dient an dieser Stelle folgende Klasse:

    public class Costumer
    {
        [NotNullValidator]
        public string Name { get; set; }

        public int Income { get; set; }
        public virtual float Discount { get { return 0.0f; } }
    }

Das Attribut NotNullValidator soll am Ende sicherstellen, dass ein der Name zumindest auf einen leeren String gesetzt wurde.

Folgender UnitTest schlägt bis zur fertigen Implementierung fehl:

 
    [TestFixture]
    public class IntegrationTest
    {
        IService advicedService = null;
            
        [TestFixtureSetUp]
        public void Init()
        {
            IApplicationContext ctx = ContextRegistry.GetContext();
            advicedService = (IService)ctx["myService"];
        }

        [Test]
        public void ShouldBeAdvised()
        {
            PrivateCostumer pCostumer = 
				new PrivateCostumer() 
					{
						Name = null, 
	 	   				Income = 50 
					};
            Assert.That(() => advicedService.Process(pCostumer), Throws.TypeOf<ValidationException>());
        }
     }

Im TestFixtureSetUp wird Spring dazu aufgefordert, entsprechend der Konfiguration einen Kontext zu erzeugen und schließlich ein Objek mit dem Namen “myService” aus dem Kontext zu liefern. Im UnitTest (verwendet wird NUnit 2.5.x) selbst wird eine Exception erwartet. Bleibt diese aus, schlägt der Test fehl.

Die vorhandene Implementierung des Service Interfaces enthält keinerlei Abhängigkeiten zum Validation Block:

    class Service : IService
    {
        private static readonly ILog log = LogManager.GetCurrentClassLogger();

        public void Process(Costumer c)
        {
            log.Info(msg => msg("Processing Costumer {0}, {1}, {2}", c.Name, c.Income, c.Discount));
        }
    }

Damit der UnitTest nicht länger fehl schlägt, wird ein Advice implementiert, der mit Hilfe des Validation Blocks alle an eine Methode übergebenen Argumente validiert und eine Exception wirft, sollt ein Argument nicht valide sein:

using AopAlliance.Aop;
using Microsoft.Practices.EnterpriseLibrary.Validation;
using Microsoft.Practices.EnterpriseLibrary.Validation.Validators;
using Spring.Aop;

namespace EntLibValidationInterceptor
{
    public class EntLibValidationInterceptor : IMethodBeforeAdvice, IBeforeAdvice, IAdvice
    {
        public ValidatorFactory ValidatorFactory { get; set; }

        public void Before(System.Reflection.MethodInfo method, object[] args, object target)
        {
            if(args == null)
		return; // args will be null when void methods are advised.
            Validator validator = CreateValidator();
            ValidationResults validationResults = new ValidationResults();
            
            foreach (object parameter in args)
            {
                validator.Validate(parameter, validationResults);
            }

            if (!validationResults.IsValid)
            {
                throw new ValidationException(validationResults);
            }
        }

        private Validator CreateValidator()
        {
            Validator validator = ValidatorFactory != null ? new ObjectValidator(ValidatorFactory) : new ObjectValidator();
            return validator;
        }
    }
}

Anstelle einer normalen Service-Instanz muss der Container angewiesen werden nun nur noch Instanzen auszuliefern, deren Methodenaufrufe durch den EntLibValidationInterceptor geleitet werden. Die folgende app.config weist Spring an, lediglich Proxy-Objekte auszuliefern:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <sectionGroup name="spring">
      <section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core" />
      <section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" />
    </sectionGroup>
  </configSections>
  <spring>
    <context>
      <resource uri="config://spring/objects" />
    </context>
    <objects xmlns="http://www.springframework.net">
      <object id="ValidationAdvice"
              type="EntLibValidationInterceptor.EntLibValidationInterceptor, EntLibValidationInterceptor" />

      <object id="myService" type="Spring.Aop.Framework.ProxyFactoryObject">
        <property name="Target">
          <object type="EntLiValidationInterceptor.Tests.DummyDomainClasses.Service, EntLiValidationInterceptor.Tests" />
        </property>
        <property name="InterceptorNames">
          <list>
            <value>ValidationAdvice</value>
          </list>
        </property>
      </object>
    </objects>
  </spring>
</configuration>

In der vorliegenden Konfiguration werden alle Methoden der Implementierten Interfaces durch den ValidationAdvice umspannt. Es ist auch Möglich, Pointcuts nur auf bestimmte Methoden deren Name auf einen regulären Ausdruck passen zu definieren:

<object id="ValidationAdvice" type="Spring.Aop.Support.RegularExpressionMethodPointcutAdvisor, Spring.Aop">
        <property name="advice">
          <object type="EntLibValidationInterceptor.EntLibValidationInterceptor, EntLibValidationInterceptor" />
        </property>
        <property name="patterns">
          <list>
            <value>.*rocess</value>
          </list>
        </property>
      </object>

oder aber nur diejenigen Methoden mit einem Aspekt zu versehen, die ein bestimmtes Attribut, beispielsweise das BaseValidationAttribute aus dem Namensraum Microsoft.Practices.EnterpriseLibrary.Validation.Validators tragen:

<object id="ValidationAdvice" type="Spring.Aop.Support.AttributeMatchMethodPointcutAdvisor, Spring.Aop">
        <property name="advice">
          <object type="EntLibValidationInterceptor.EntLibValidationInterceptor, EntLibValidationInterceptor"/>
        </property>
        <property name="attribute" value="Microsoft.Practices.EnterpriseLibrary.Validation.Validators.BaseValidationAttribute, Microsoft.Practices.EnterpriseLibrary.Validation" />
      </object>

Update: Da hat sich doch tatsächlich ein Fehler eingeschlichen. Die object[] args sind bei advisten-Methoden ohne Parameter zur Laufzeit null. Also sollte man darauf entsprechend reagieren.

Donnerstag, 16. Dezember 2010

Outlook 2007: Keine Anmeldung am Email Server möglich

Seit gestern ist das installierte Outlook 2007 nicht mehr in der Lage, Email von Pop3 oder Imap Servern zu empfangen. Auch das Senden funktioniert nicht. Nach einiger Suche im Windows Update Logbuch vermute ich einen Zusammenhang mit dem Windows Update KB2412171 das am 14 Dezember 2010 ausgerollt wurde.

Seit dem Update erhalte ich u.a. folgende Fehlermeldung:

Fehler bei der Allgemein-Authentifizierung. Keine der Authentifizierungsmethoden, die vom IMAP-Server unterstützt werden, wird von diesem Computer unterstützt.

und beim Sendeversuch:

Vom Server wird keine der von diesem Client unterstützten Authentifizierungsmethoden unterstützt.

Andere Nutzer beklagen sich über langsame Ordnerzugriffe nach dem Update oder raten von einer Installation ab.

Ein möglicher Workaround um wieder Emails zu empfangen geht leider auf Kosten der Sicherheit: Deaktiviert man in den Email-Kontoeinstellungen die Option “Anmeldung mithilfe der gesicherten Kennwortauthentifizierung (SPA) erforderlich”, klappt der Emailempfang.

Update 12.01.2011

Microsoft hat zwar bereits am 21.12.2010 eingesehen, dass es nach dem Office 2007 Update vom 14.12.2010 zu folgenden Probleme kommen konnte:

    • Es kommt zu einer Verzögerung beim Wechseln zwischen E-Mail-Ordnern im Navigationsbereich.
    • Die Autoarchivierungsfunktion ist nicht mehr verfügbar.
    • Es treten Fehler beim Senden oder Empfangen von E-Mail-Nachrichten auf.

veröffentlicht aber mit dem KB2412171 erst gestern ein entsprechendes Update, welches die Fehler korrigieren soll.

Donnerstag, 9. September 2010

Outlook 2007: Email drucken ohne Benutzername im Memo Stil

Wer Outlook benutzt und dort mehrere E-Mail-Konten verwaltet und auch gelegentlich eine Email ausdrucken will, wird das kennen: Dank des Memo-Druckstils prangt über dem Ausdruck der Benutzername:

image

Überflüssig und nervig, denn es ist nicht der Name des Empfängerkontos der dort steht, sondern der Anzeigename des Hauptkontos bzw. der Inhalt des “Ihr Name” Feldes des Standardkontos.

Wenn man, wie ich, nur selten Emails ausdruckt, gelingt einem diese Assoziation nicht sofort aber schließlich wurde ich bei Microsoft im Supportartikel “Cannot Omit or Change Name When Printing Memo Style” fündig. Leider lässt sich der erste Tipp zumindest bei meinem Word 2007 nicht durchführen: die per Email/Speicher unter erstellte .msg-Datei lässt sich in Word 2007 nicht korrekt öffnen – obwohl die Voransicht vielversprechend aussieht:

image

Die weiteren Tipps funktionieren jedoch und so kann man per Extras/Kontoeinstellungen relativ zügig ein anderes Konto “Als Standard festlegen” woraufhin der Inhalt des Feldes “Ihr Name:” über dem Memo Ausdruck erscheint.

image

Sonntag, 11. Juli 2010

Das Outlook-Fenster kann nicht geöffnet werden

Ich habe keine Ahnung, was ich gemacht habe aber seit Neustem startet Microsoft Office Outlook 2007 unter Windows7 nicht mehr und es erscheint stattdessen die Fehlermeldung:

Microsoft Office Outlook kann nicht gestartet werden. Das Outlook-Fenster kann nicht geöffnet werden.

outlook_resetnavpaneDie Lösung musste ich ständig mühsam ergooglen; nun merke ich mir sie hier. Dazu muss man Outlook einfach per

outlook.exe /resetnavpane

starten (Windows-Taste + R) und schon funktioniert das wuchtige Mailprogramm wieder.

Sonntag, 4. Juli 2010

Asus M2N32 Deluxe, AMD Phenom II, C1E und die Systemuhr

Mein Motherboard Asus M2N32 Sli Deluxe mit AM2 Sockel ist zwar schon mehr als vier Jahre alt aber da AM3 Prozessoren abwärtskompatibel zu AM2 Sockeln sind und Asus freundlicher Weise auch an Kunden denkt, die einst viel Geld für ein Mainboard bezahlt haben, konnte ich einen aktuellen AMD Phenom II X4 945 (C3) mit 3.0 GhZ und 8.0 MB Cache verbauen. Eine Liste von unterstützen Prozessoren gibt es auf der Asus Seite.

Hierzu war zunächst ein Biosupdate auf die Beta Version 5002  notwendig, welches mit USB Stick und Asus EZ-Flash problemlos möglich war. Windows7 wollte nach dem ersten Systemstart gleich nochmal neustarten, danach lief alles reibungslos und sehr viel schneller als mit dem alten AMD 64 X2 4200+ Prozessor.

Lediglich die Systemuhr schien nun sehr viel langsamer zu ticken, sodass nach einigen Stunden Laufzeit eine Differenz von mehreren Minuten zwischen Systemzeit und Funkuhr zutage traten.

Nach Abschalten der C1E Funktion im Bios läuft die Zeit nun wieder gleichmäßig. Die AMD Cool’n’Quiet Funktion ist weiterhin aktiv.

Bei der Gelegenheit habe ich dann auch gleich die Windows eigene Zeitsyncronisierung durch die NTP Portierung von Meinberg ersetzt.

Samstag, 8. Mai 2010

Tastaturkürzel für Visual Studio 2010

Nachdem ich mir meine Lieblingstastaturkürzel für Visual Studio 2008 mit Hilfe dieser Keybinding Reference merken konnte, habe ich gerade durch Zufall die Tastenbelegung für die neue Visual Studio 2010 Version auf der Microsoft Downloadseite gefunden. Die “Visual Studio 2010 Keybinding Cards” beinhalten die gängigen Tastenbelegungen zur Entwicklung mit den Sprachen Visual Basic, C#, C++ und F#.

Nervende SMS von der Nummer 221

Mittlerweile bin ich stolzer Besitzer eines “SuperPhones” mit Googles Android Betriebssystem. Zum neuen Handy muss auch ein geeigneter Vertrag her. Kaum habe ich die neue Simkarte eingelegt, erhalte ich merkwürdige SMS vom Absender 221. Der Inhalt besteht aus zwölf weiteren Zahlen. Nach einiger Suche habe ich schließlich den Wikiartikel zum Thema Cell Broadcast (CB) gefunden. Nun ist mir klar, was die mystischen Zahlenkolonnen zu bedeuten haben die ich bekomme seit dem ich einen O2 Vertrag habe: Es handelt sich um die Gauß-Krüger-Koordinaten des Sendemasten.

Da die ständigen Nachrichten ein wenig stören, habe ich die den Empfang von Cell Broadcasts in meinem HTC Desire ausgeschaltet:

  • Menü taste drücken und Einstellungen auswählen
  • Menüpunkt “Anrufen” auswählen
  • Unter “Kurznachricht” den  Haken abwählen, sodass “Empfang von CB-Nachrichten deaktivieren” erscheint.

Ansonsten bin ich mit dem Gerät und dem Betriebssystem fast zufrieden: Das Synchronisieren der Kontakte ohne Google Dienste funktioniert noch nicht, da die mitgelieferte Software “HTC Sync” unter meinem Windows 64 nicht startet – aber es besteht noch die Hoffnung, dass sich die Android Community um ähnliche Features kümmert, nachdem sie die nicht minder wichtigen Features implementiert hat (ein Speichern von Applikationen auf der SD-Karte ist Beispielsweise immer noch nicht möglich).