Author Archive for admin

POS-Tags und Lemmatisierung

Richtig zufrieden bin ich mit dem Porter-Stemmer-Algorithmus nicht. Er erkennt den gemeinsamen Wortstamm sehr gut, aber so habe ich es mir nicht vorgestellt. Er macht aus z.B. Töchterchen, Tochter, Töchter den Stamm tocht, und aus Brüder, Bruder, brüderlich, brüderlicherseits den Stamm brud, aber aus sehen macht er seh und aus sahen und sah wird sah. Ich wünsche mir aber Worte, die ich auch im Wörterbuch nachschlage und wenn ich nach sah oder sahen suche, schaue ich unter sehen. Oder wenn ich nach sprechen oder gesprochen oder sprach suche, schaue ich unter sprechen. Diese Grundformen nennt man aber Lemma und es existieren Programme, nämlich Lemmatisierer, um Wörter auf ihre Grundform, ihre Wörterbuchform, zurück zu führen. Außerdem will ich es jetzt wirklich wissen und mich interessieren auch die POS (Part of Speech)-Tags eines Satzes. Diese Tags sagen mir, ob ein Wort ein Nomen, ein Name oder eines von 10 Arten von Verben oder ein bestimmtes Adjektiv oder Adverb ist. Jetzt, wo ich schon am Thema NLP (Natural Language Processing) bin, kann ich mir das Gebiet ja auch zu Nutze machen. Als POS-Tagger, die für deutsche Sätze funktionieren habe ich nur den Stanford POS-Tagger und den IMS Tree Tagger gefunden. Nur der IMS Tree Tagger vermag es aber, mir auch das Lemma eines Wortes zu geben.

Im nachfolgenden Codebeispiel, zerlegt mir der Stanford POS-Tagger einen Satz in Tokens gibt dann für jedes Token dessen POS-Tag aus. Die Quellcodebeispiele sind alle in Java, welches die vorherrschende Sprache im NLP zu sein scheint. Es muss wohl doch keine so langsamen Sprache sein. Allerdings erledigen alle Programme, die ich gefunden habe, die Arbeit statisch. Sie sind nicht in andere Software eingebunden, sondern ein Text wird durch NLP-Software rechenintensiv verarbeitet und mit dem Ausgabetext kann man dann machen, was man will.

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;

import edu.stanford.nlp.ling.Sentence;
import edu.stanford.nlp.ling.TaggedWord;
import edu.stanford.nlp.ling.HasWord;
import edu.stanford.nlp.tagger.maxent.MaxentTagger;

class TaggerDemo {

  public static void main(String[] args) throws Exception {

    MaxentTagger tagger = new MaxentTagger("models/german-hgc.tagger");
	String s = "Aber alle Griechen nahmen Sosthenes, den Synagogenvorsteher, mit sich und schlugen ihn vor dem Richterstuhl. Und Gallio kümmerte sich um keinen dieser (Vorgänge).";
	InputStream is = new ByteArrayInputStream(s.getBytes());
	BufferedReader br = new BufferedReader(new InputStreamReader(is));

    List<List<HasWord>> sentences = tagger.tokenizeText(br);

    for (List<HasWord> sentence : sentences) {
      ArrayList<TaggedWord> tSentence = tagger.tagSentence(sentence);
      System.out.println(Sentence.listToString(tSentence, false));
    }
  }

}

Die Ausgabe:
Aber/KON alle/PIDAT Griechen/NN nahmen/VVFIN Sosthenes/NE ,/$, den/ART Synagogenvorsteher/NN ,/$, mit/APPR sich/PRF und/KON schlugen/VVFIN ihn/PPER vor/APPR dem/ART Richterstuhl/NN ./$.
Und/KON Gallio/NE kümmerte/VVFIN sich/PRF um/APPR keinen/PIAT dieser/PDAT -LRB-/TRUNC Vorgänge/NN -RRB-/TRUNC ./$.

Schade, dass es für den Stanford POS Tagger keinen Lemmatisierer gibt. Den Tokenizer, welcher mir Sätze in Tokens zerlegt kann ich aber gebrauchen. So etwas muss ich ja nicht selbst programmieren. Im nachfolgenden Codeschnipsel tut der IMS Tree Tagger seinen Dienst und gibt sowohl Lemmas als auch POS-Tags aus.

import static java.util.Arrays.asList;

import org.annolab.tt4j.TokenHandler;
import org.annolab.tt4j.TreeTaggerWrapper;

public class tt4j {
        public static void main(String[] args) throws Exception {
                // Point TT4J to the TreeTagger installation directory. The executable is expected
                // in the "bin" subdirectory - in this example at "/opt/treetagger/bin/tree-tagger"
                System.setProperty("treetagger.home", "/home/bileser/Downloads/IMSTreeTagger/tree-tagger-linux-3.2");
                TreeTaggerWrapper<String> tt = new TreeTaggerWrapper<String>();
                try {
                        tt.setModel("/home/bileser/Downloads/IMSTreeTagger/tree-tagger-linux-3.2/german.par:iso8859-1");
                        tt.setHandler(new TokenHandler<String>() {
                                public void token(String token, String pos, String lemma) {
                                        System.out.println(token + "\t" + pos + "\t" + lemma);
                                }
                        });

                        String[] list = new String[] {
                        		"Und",
                        		"Gott",
                        		"sah",
                        		",",
                        		"dass",
                        		"es",
                        		"gut",
                        		"war",
                        		"."

                        };

                        tt.process(asList(list));
                }
                finally {
                        tt.destroy();
                }
        }
}

Die Ausgabe:
Und KON und
Gott NN Gott
sah VVFIN sehen
, $, ,
dass KOUS dass
es PPER es
gut ADJD gut
war VAFIN sein
. $. .

Durch POS-Tags und Lemmatisierung habe ich vor, Lückentexte zu generieren und rechnerisch die wichtigsten Sätze von Texten anhand der Häufigkeiten der Lemmas von Nomen, Verben und anderen Satzbestandteilen zu ermitteln. Zu den Themen automatische Generierung von Zusammenfassungen oder Fragen findet man im Netz allerhand Papers. So tief will ich denn in die Wissenschaft nicht eintauchen.

Groß, größer, am größten

Mein erstes Vorhaben ist abgearbeitet. Ich habe die Wörter der Bibel aufbereitet, indem ich alle Stopwörter herausgefiltert und jedes Wort auf seinen Worstamm mit dem Porter-Stemmer-Algorithmus zurückgeführt habe. Das Ergebnis beeindruckt mich selbst. Die Stopwortliste ist gut. Das häufigste Wort ist nicht und, oder, dass, mein, dein, …, sondern Gott. Folgendes Bild zeigt meine kleine Anwendung:

Bildschirmfoto am 2012-03-24 13:23:49

Das Bild zeigt, dass der Porter-Stemmer ganz gut funktioniert, oder? groß, große, großem, großen, großer, großes, größe, größer, größere, größeren werden auf den Wortstamm gross zurück geführt. Der Algorithmus kennt allerdings nicht den gemeinsamen Stamm von sprach und spricht. Meine Bibelübersetzung ist wohl zu alt, den er kennt auch nicht den gemeinsamen Stamm von herr und herrn. Aber wie soll man einem Programm eine willkürliche Grammatik und Zeitformen beibringen? Es werden immer Fehler bleiben. Die Häufigkeiten der Wortstämme sind auf dem Bild spiralenförmig angeordnet. Gott ist das häufigste Wort in der Bibel, wen wunderts. Nun ja, das mag aber dennoch Menschen in Erstaunen setzen, die meinen, es gehe in der Bibel hauptsächlich um die Liebe oder Verhaltensregeln unter den Menschen. Interessant auch, dass Gott so viel spricht. Nun ja, mindestens wird jetzt deutlich, dass ich heimlicher Bibelfan bin.

An nackte Pointer in den folgenen Zeilen muss ich mich in Qt noch gewöhnen. In Qt übernimmt das Container-Element die Verantwortung für das Löschen der innewohnenden Elemente. Smartpointer brauche ich also nur für meine Datenstrukturen.

QStandardItem *child2 = new QStandardItem( QString::fromUtf8(verse.c_str()));
item->setChild(i,1,child2 );

Ich habe noch einmal gelernt, wie wichtig es in C++ ist, Variablen grundsäzlich zu initialisieren. Es müssen nicht nur alle Klassenelemente in der Initialisierungsliste initialisiert werden, sondern auch lokale Variablen in Methoden. Im Debugmodus hatte ich in Qt den Fall, dass eine uninitialisierte Variable automatisch mit 0 initialisiert wurde, so dass ich davon ausging, das der Code läuft. Im Releasemodus wurde diese Nettigkeit jedoch wegoptimiert, so dass ich den Fehler suchen musste.

Wer sich mit eigenen Augen überzeugen möchte, hier ist das CtCreator-Projekt.

Multithreading …

Kann ich vielleicht die neu in den C++-Standard integrierten Threads für mein Projekt verwenden? Ich tippe mal folgendes Beispiel aus der iX ab und messe die Zeit der Ausführung, für einen Thread und für beide Threads, mit std::lock_guard und Zugriff auf gemeinsame Variable und ohne:

#include <iostream>
#include <thread>
#include <time.h>

int counter;
const int NCOUNTS = 10000000000;
std::mutex incrementMutex;

void increment(){
    for (int i=0; i<NCOUNTS; i++){
        std::lock_guard<std::mutex> lock(incrementMutex);
        counter++;
        std::cout << counter << std::endl;
    }
}

void decrement(){
    for (int i=0; i<NCOUNTS; i++){
        std::lock_guard<std::mutex> lock(incrementMutex);
        counter--;
        std::cout << counter << std::endl;
    }
}

main(){

    time_t seconds = time (NULL);

    std::thread a(increment);
    std::thread b(decrement);
    a.join();
    b.join();

    time_t seconds2 = time(NULL);
    std::cout << std::endl << seconds2-seconds << std::endl;
}

Mein aktueller g++ kommt ja schon zum großen Teil mit den neuen C++11-Features zurecht, so dass ich mit g++ -std=c++0x -pthread thread.cpp -o thread das Programm kompilieren kann. Der ich noch nicht so viel mit dem gefürchteten Thema des gemeinsamen Zugriffs von Threads auf Ressourcen zu tun hatte, bin überrascht. Benutze ich std::lock_guard und lasse beide Threads gleichzeitig die Variable count manipulieren, hört die Ausführung mit diesem NCOUNTS nicht mehr in vertretbarer Zeit auf. Ohne gemeinsamen Variablenzugriff hingegen, dauert die Ausführung 4 Sekunden. Eine Zeitersparnis hat man also nur ohne Zugriff auf gemeinsame Ressourcen. Wenn ohne diesen Zugriff beide Threads so ideal wie hier das selbe tun, kann man dies wunderbar parallelisieren. Man sieht schön in der Systemüberwachung und anhand der Ausgabe der benötigten Zeitdauer, dass hier in gleicher Zeit das Doppelte getan wird. Aber wo kann man in der Realität eine Aufgabe in genau gleiche Teile teilen? Mal schauen, ob ich diese Verwendung noch in meiner Programmiererei unterbringen kann … Threads sind aber auch da sinnvoll, wo Multitasking ermöglicht werden soll. Android schreibt z.B. vor, dass länger dauernde Prozesse nicht im GUI-Thread laufen, sondern parallelisiert werden sollen. Wird die Ausführung der Oberfläche auch für einige Sekunden unterbrochen, sieht der Benutzer den gefürchteten ANR (Application not responding)-Dialog. Natürlich läuft eine Oberfläche flüssiger, wenn Hintergrundprozesse auch in einem eigenen Thread laufen.

Gutes altes C++, Qt und QtCreator

Zurück zum guten alten C++. Was soll dieser ganze neumodische Schnickschnack eigentlich? Was Grafischs soll es diesmal sein. Ich visualisiere ein Buch. Ich gehe jedes einzelne Wort des Buches durch. Wenn es sich in einer Stopwortliste befindet, wird es ignoriert. Stopworte sind bedeutungslose Wörter wie sein, ist, wird, war, ein, auf, … Die auf diese Weise gefunden natürlichen Wörter werden gezählt und gemerkt. In dieser Liste gehören aber immer noch viele Worte zum selben Wortstamm. Z.B. gehören laufen, gelaufen, lief, lauf(!) zum selben Stamm. Es gibt einen Algorithmus, den Porter Stemmer, der Worte anhand irgendwelcher Regeln auf ihren Wortstamm zurück führt. Dieser Algorithmus wird in der Computerlinguistik und im Information Retrievel angewendet. Perfekt geht das natürlich nicht. Auch Suchmaschinen ignorieren Stopwörter und betrachten die Stämme von Wörtern, um die Vielfalt von Worten zu reduzieren. Die nun gewonnenen gestemmten Wörter will ich nun der Häufigkeit nach sortieren und visualisieren. Die Wörter breiten sich spiralförmig von der Mitte bis zum Rand aus. In der Mitte befinden sich die Wörter, die am häufigsten im zu betrachtenden Text vorkommen und am Rand die seltensten Wörter des Textes. Nun kann man in diese Wortebene hinein und herauszoomen. Klickt man auf ein Wort, soll man grafisch sehen, welche anderen Worte mit diesem Wort am häufigsten im selben Satz/Kapitel vorkommen. Bis dahin erstmal. Wofür das Ganze? Weil ich Lust habe, etwas zu programmieren. Weil die geforderte Geschwindigkeit der vielen Berechnungen und die Geschmeidigkeit der grafischen Ansicht C++ erfordern. Und es gibt bestimmt Bücher, die man so meditativ betrachten kann. Zum Einsatz kommen Qt und QtCreator, da ich unter Linux entwickeln will. Bisher habe ich den Porter Stemmer, welcher in C geschrieben ist, plattgeklopft, in ein Verzeichnis portiert und in mein QtCreator-Project integriert. Der Algorithmus ist pfeilschnell und hier lobe ich mir C. Ich brauche ja hier nicht viel Hand an die Pointerarithmetik und an Arrayoperatonen anlegen zu müssen. Aber eine Liste von 35.000 Wörtern ist in Null, Nichts gestemmt. Ich habe mir eine Stopwortliste aus heruntergeladenen Stopwortlisten generiert und meine Datenstrukturen in Headerdateien festgehalten. Bisher habe schon einige STL-Container verwendet: set, vector, map. Das kleine Projekt scheint also einiges zum Spielen zu bieten. QtCreator finde ich gar nicht schlecht. Kleinere Sachen stoßen mir übel auf. Warum kann man z.B. in der Projektansicht nicht mehrere Dateien auswählen und löschen? Nein, es geht nur einzeln. Beim Debuggen von Konsolenanwendungen bekomme ich eine Fehlermeldung. Nachforschungen zufolge, könnte ich diese oder jene Einstellung in einer Datei ändern. Na gut, dann eben keine Konsolenanwendung. Entgegen der Menübeschreibung kann ich einen markierten Text mit Strg+/ nicht auskommentieren, sondern die Hilfe ploppt auf. Nun ja, von solchen kleinen Dingen abgesehen, klappt die Entwicklung aber ganz gut. Ich kann mit QtCreator schön zwischen der Definition und der Deklaration von Methoden und Variablen hin- und herspringen, eine Funktion, die Visual Studio nur in Verbindung mit Visual Assist bietet. Grafisch sieht die IDE ganz gut aus, unter Linux ist man anderes gewohnt. Qt scheint also auch nicht schlecht zu sein. Der Editor mit seiner Syntaxvervollständigung ist geschmeidig. Funktionen, wie Funktionsrümpfe anhand des Headers zu erstellen, vermisse ich. Es ist also etwas Handarbeit nötig. Warum kann ich die kleinen Fensterchen Haltepunkte, Aufrufliste, Lokale Variablen und Ausdrücke … nicht wie bei Eclipse oder Visual Studio zu einem Fenster mit verschiedenen Tabs vereinigen und auf einen anderen Bildschirm schieben? Es gibt viele Kleinigkeiten, die nicht so ganz laufen, und die Kleinigkeiten machen den Komfort aus. Aber das ist unter Linux der Preis der Freiheit. Schließlich bin ich ja bei Qt und wenn es mich nervt, kann ich nach Windows und zu anderen IDE wechseln.
Da ich früher bei der Arbeit immer mit CString gearbeitet habe, frage ich mich jetzt, wie ich mit std::string umgehen soll. Etwa gemäß RAII (Resource Acqusition is Initialization) vorgehen und in shared_ptr verpacken? In Effective STL von Scott Meyers habe ich dann aber noch mal nachgelesen, dass die STL ihre Objekte selbst verwaltet. Genauso, wie ich vector-Objekte selbst nicht lösche, muss ich mich auch nicht um das dynamische Erzeugen und das Löschen von string-Objekten kümmern.

Daily Activity

Mein Ziel: Eine garantierte Activity pro Tag, nämlich wenn das Smartphone angeschaltet wird und die Activity zum Einschaltdatum noch nicht aufgerufen wurde. Solche Dinge sollte man natürlich sehr bewusst programmieren, um keinen Benutzer zu nerven. In diesem Fall bin ich aber der Benutzer.
In der Referenz des Android SDK finde ich ein Systemereignis, welches in Frage kommt, um von einem Broadcast Receiver empfangen zu werden: ACTION_SCREEN_ON. Während ich mittels der AndroidManifest.xml für viele Ereignisse ganz zentral Broadcast Receiver während der Installation einer Anwendung im Betriebssystem registrieren kann, ist dies aber ausgerechnet für dieses Ereignis nicht möglich. Ich habe einige Codebeispiele gesehen, in denen Broadcast Receiver in der OnCreate von Activities registriert wurden. Doch möchte ich den Receiver von der aufgerufenen Activity entkoppeln. Nach weiterer Suche entdecke ich das Ereignis ACTION_USER_PRESENT, welches auch ein mittels der AndroidManifest.xml registrierter Broadcast Receiver empfangen kann.

Wird das Ereignis mit nachfolgendem ActivityLauncher empfangen, wird das Datum des heutigen Tages mit dem eingelesenen Datum aus einer Datei verglichen. Unterscheiden sich die Angaben, wird das aktuelle Datum in die Datei geschrieben und die Activity wird aufgerufen.

public class ActivityLauncher extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {

        if (! (intent.getAction().equals(Intent.ACTION_USER_PRESENT)))
          return;

        String date = GetDateFromFile(context);

        Date dateNow = new Date ();
        SimpleDateFormat dateformatYYYYMMDD = new SimpleDateFormat("yyyyMMdd");
        StringBuilder nowYYYYMMDD = new StringBuilder( dateformatYYYYMMDD.format( dateNow ) );
        String now = nowYYYYMMDD.toString();

        if (date.compareTo(now) != 0)
        {
          WriteCurrentDateToFile(context, now);

            Intent startActivity = new Intent();
            startActivity.setClass(context, MyActivity.class);
            startActivity.setAction(MyActivity.class.getName());
            startActivity.setFlags(
                    Intent.FLAG_ACTIVITY_NEW_TASK
                    | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
            context.startActivity(startActivity);
        }
    }
}

Hallo, Shared SQLite DB

Nachdem ich nun die fundamentalen Dinge über die Android-Entwicklung gelesen habe, mache ich mir jetzt die Hände schmutzig. Ich habe die ersten Beispiele und Tutorials aus den einschlägigen Quellen, nämlich developer.android.com, Hello, Android von Ed Burnette und Professional Android Development von Reto Meier ausprobiert, und komme zu dem Ergebnis, dass ich meine Hände auch schmutzig lassen kann. Schließlich geht es hier um Best Practices und die Beispiele haben vorbildlichen Character.
Wie man schon am Hello World-Tutorial erkennen kann, trennt man das Layout seiner Oberfläche am Besten in Form von XML vom Inhalt ab. Ähnlich geht man in der Webentwicklung vor, wo man HTML von CSS trennt. In der Android-Entwicklung hat das den Vorteil, dass man verschiedene Layouts für verschiedene Bildschirme und Geräte entwickeln kann und dabei vom Android SDK auch unterstützt wird.
Auch der Lokalisierung wird von Anfang an Rechnung getragen, indem Textstrings der Oberfläche nur per ID aus dem Layout referenziert werden. Für jede Sprache stellt Google eine Dateierweiterung bereit, so dass die Textstrings aus der richtigen Datei eingelesen werden.

Nun stelle ich mir die Frage, wie ich Daten zwischen Activities teilen kann. Mein erster Gedanke war, die Daten in Form einer XML-Datei zu speichern und für die Anwendung in eine Objektstruktur einzulesen, so dass eine schneller Zugriff möglich ist. Ein bevorzugter Weg, Persistenz in Android zu ermöglichen, ist aber die Verwendung einer SQLite-Datenbank. Auf reigndesign habe ich einen Weg gefunden eine Datenbank von außen beizusteuern. Statt die DB von innen über das Android-SDK anlegen zu müssen, kann ich die Datenbank in das assets-Verzeichnis des Projekts legen.

Ich realisiere meinen von SQLiteOpenHelper abgeleiteten DBHelper als Singleton. Auch wenn es nichts kosten mag, in jeder Activity einen DBHelper zu initialisieren, muss ich mit einem Singleton nicht einmal über die Kosten von Objekterzeugungen nachdenken, denn ein paar Zeilen Code für die Erstellung eines Singleton kosten schon gar nichts. Ich spare mir auch die Mühe, in jeder Activity an jeder in Frage kommenden Stelle des Activity-Lebenszyklus die Datenbank öffnen und schließen zu müssen. Stattdessen stelle ich in meiner abgeleiteten Application-Klasse MyApplication eine Referenz auf eine DBHelper-Instanz bereit, über die ich von jeder Activity aus auf die Datenbank zugreifen kann.

Der Zugriff aus der Activity sieht dann für einen schreibenden Zugriff etwa so aus:


package org.example.hellosqlite;

import static android.provider.BaseColumns._ID;
import android.app.Activity;
import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.widget.TextView;

public class HelloSQLite extends Activity
{
	private static final String TITLE = "title";
	public static final String TABLE_NAME = "books";
	private static String[] FROM = { _ID, TITLE, };

	@Override
	public void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);

		addBook("Hello, Android! 3e");
	}

	private DataBaseHelper DBH()
	{
		MyApplication application = ((MyApplication)getApplicationContext());
		return application.GetDataBaseHelper();
	}

	private void addBook(String string)
	{
		SQLiteDatabase sqlitedb = DBH().getWritableDatabase();
		ContentValues values = new ContentValues();
		values.put(TITLE, string);
		sqlitedb.insertOrThrow(TABLE_NAME, null, values);
	}
}

Reich mit Amazon Product Advertising API?

Unter der Amazon Product Advertising API wird dem Programmierer eine Untermenge der Funktionalität des Amazon-Shops zur Verfügung gestellt. Amazon-Produktdaten können nahtlos in die eigene Website integriert werden. Produkte können z.B. in einen Warenkorb gelegt werden und der Besucher kann sich umfangreich über die Produkte informieren. Die Bestellabwicklung übernimmt Amazon. Amazon stellt keine Reviews mehr zu Verfügung. Die müssen auf der Eigentümersite gelesen werden. Auf diese Veränderung weist Amazon deutlich hin. Als weitere Änderung muss auf eine bestimmte Anzahl an Anfragen über die API auch ein gewisser Prozentsatz an Bestellungen erfolgen. Ich sehe die Veränderungen als Amazons Reaktion darauf, dass die Produktdaten in der Vergangenheit fleißig von Suchmaschinenoptimierern (SEO) missbraucht wurden, um Inhalte für Websites zu generieren. Wofür braucht das Internet millionenfach reproduzierten Content, der doch ursprünglich einzig und alleine Amazon gehört? So weisen die License Agreements zur Benutzung dieser API denn auch darauf hin, dass die API nur dazu benutzt werden soll, um Besucher zu Amazon umzuleiten.

Hier studiere ich ein C#-Codebeispiel, welches ich in der Amazonschen Dokumentation gefunden habe. Es handelt von einer REST-Anfrage an den Amazon-Server. Die Antwort kommt als XML zurück. Um den Eigentümer der Anfrage eindeutig zu identifizieren bietet Amazon verschiedene Verfahren an. Das Beispiel setzt ein Verfahren um, bei dem Amazon und der Programmierer den gleichen geheimen Schlüssen haben. Dieser Schlüssel kann auf Knopfdruck neu genieriert werden.

Das Beispiel benutzt die Klasse HMACSHA256. Instanzen dieser Klasse können einen Hash-based Message Authentication Code (HMAC) mit dem SHA256-Algorithmus erzeugen.

Zuerst wird eine Instanz von HMACSHA256 mit dem geheimen Schlüssel als Argument erzeugt:


string secretKey = "XblaY"
HMAC signer = new HMACSHA256(secretKey);

Danach wird der gesamte Query-String signiert:


string queryString = "Fertig formatierter String mit allen gewünschten Parametern, für Amazon präpariert";
byte[] sigBytes = signer.ComputeHash(queryString );
string signature = Convert.ToBase64String(sigBytes);

Diese Signatur wird dann als letzter Parameter der Anfrage angehängt.

Kann ich jetzt endlich durch die Benutzung der API reich werden? Meine Nachforschungen haben ergeben, dass weniger Programmieren mehr ist. Besucher einer Website wurden noch nie durch Programmierkunst beeindruckt, sondern durch handgemachten Inhalt. So habe ich denn auch unter denjenigen, die einen nennenswerten Verdienst durch das Partnerprogramm erzielten, nur solche gefunden, die vereinzelte Links auf Amazonprodukte gesetzt haben. Diese Links haben direkt etwas mit dem zu tun, was sie in mühsamer Arbeit gepostet haben. Hier wird es eine vollautomatische Generierung von Produktdaten schwer haben, zu Verdienst gemacht zu werden. Da besucht man doch lieber direkt den Amazon-Shop.

Also, keine Scham, das Partnerprogramm ohne API zu nutzen! Die einfachste Möglichkeit besteht darin, einen Link auf die Amazon-Homepage zu setzen. Damit Amazon nicht zu viele für Suchmaschinen sichtbare Links bekommt, können Links umgeleitet werden. In ASP.NET MVC habe ich das so gemacht:

    public class AmazonLinkController : Controller
    {
        //
        // GET: /AmazonLink/

        public void Index()
        {
            Response.Redirect("http://www.amazon.de/exec/obidos/redirect-home?tag=meinTag-21&site=home");
        }

    }

die Action Index meines Controllers ist eine void-Methode und leitet zu Amazon um.

URL-Rewriting mit ASP.NET MVC

Möchte man ASP.NET MVC einsetzen, um Internetanwendungen zu programmieren, kann man guten Inhalt direkt mit Suchmaschinenoptimierung verknüpfen. Öffentlich zugänglicher Inhalt kann auch Google möglichst gut zugänglich gemacht werden. Die Google-Roboter, die Websites abgrasen und Webseiten in den Index aufnehmen, scheuen vor URL mit Parametern zurück (z.B. www.wetter.de?PLZ=53127) und nehmen statisch erscheinende URL in den Index auf (z.B. www.wetter/53127.html). Das ist ein triftiger Grund, URL statisch aussehen zu lassen. Zusätzlich sehen statische URL freundlicher aus, und können besser gemerkt werden.

Als Erstes definiere ich eine Zuordnung zwischen einem Konfigurationsabschnittshandler und einem Konfigurationsabschnitt. Aus diesem Grund füge ich dem configSections-Knoten in Web.config folgende sectionGroup hinzu:

<configSections>
  <sectionGroup name="myModuleConfig">
    <section name="rewriteModule" type="MyAssembly.RewriteModuleSectionHandler, MyAssembly"/>
  </sectionGroup>
</configSections>

Dementsprechend muss ich dann im configuration-Knoten den Konfigurationsabschnitt definieren:

<myModuleConfig>
  <rewriteModule>
    <rewriteOn>true</rewriteOn>
    <rewriteRules>
      <rule source="Keyword/(.+).aspx" destination="Keyword?keyword=$1"/>
    </rewriteRules>
  </rewriteModule>
 </myModuleConfig>

In diesem Abschnitt definiere ich, ob das URL-Rewriting stattfinden soll und ich definiere Rewrite-Rules, so wie man das unter PHP in einer .htaccess-Datei tun würde.

Nun muss ich den Konfigurationsabschnittshandler RewriteModuleSectionHandler denn auch in Code gießen:

public class RewriteModuleSectionHandler : IConfigurationSectionHandler
{

    private XmlNode _XmlSection;
    private string _RewriteBase;
    private bool _RewriteOn;

    public XmlNode XmlSection
    {
        get { return _XmlSection; }
    }

    public string RewriteBase
    {
        get { return _RewriteBase; }
    }

    public bool RewriteOn
    {
        get { return _RewriteOn; }
    }
    public object Create(object parent, object configContext, System.Xml.XmlNode section)
    {
        _RewriteBase = HttpContext.Current.Request.ApplicationPath + "/";

        try
        {
            _XmlSection = section;
            _RewriteOn = Convert.ToBoolean(section.SelectSingleNode("rewriteOn").InnerText);
        }
        catch (Exception ex)
        {
            throw (new Exception("Error while processing RewriteModule configuration section.", ex));
        }
        return this;
    }
}

Im Wesentlichen nimmt mein Handler bei Erzeugung seinen XML-Knoten in Empfang und dient mir dazu, den XML-Zugriff zu kapseln.

Zusätzlich definiere ich in der Web.config ein HTTP-Modul:

<system.web>
  <httpModules>
    <add name="RewriteModule" type="MyAssembly.RewriteModule, MyAssembly"/>
  </httpModules>
</system.web>

In C#-Code gegossen:

class RewriteModule : IHttpModule
{

    public void Dispose() { }

    public void Init(HttpApplication context)
    {
        context.BeginRequest += new EventHandler(RewriteModule_BeginRequest);
    }

    void RewriteModule_BeginRequest(object sender, EventArgs e)
    {
        RewriteModuleSectionHandler cfg = (RewriteModuleSectionHandler)ConfigurationManager.GetSection("myModuleConfig/rewriteModule");

        if (!cfg.RewriteOn) return;

        string path = HttpContext.Current.Request.Path;

        if (path.Length == 0) return;

        XmlNode rules = cfg.XmlSection.SelectSingleNode("rewriteRules");
        foreach (XmlNode xml in rules.SelectNodes("rule"))
        {
            try
            {
                string attrsource = "/" + xml.Attributes["source"].InnerText;

                Regex re = new Regex(attrsource, RegexOptions.IgnoreCase);

                Match match = re.Match(path);
                if (match.Success)
                {
                    path = re.Replace(path, xml.Attributes["destination"].InnerText);
                    // new path to rewrite to
                    string rew = cfg.RewriteBase + path;

                    // rewrite
                    HttpContext.Current.RewritePath(rew);

                    return;
                }
            }
            catch (Exception ex)
            {
                throw (new Exception("Incorrect rule.", ex));
            }
        }
        return;
    }

}

in der Init-Methode, die mir das IHttpModule-Interface vorschreibt, bekomme ich eine Referenz auf die Anwendung übergeben. Ich füge dem Anwendungsevent BeginRequest als EventHandler einen Delegaten hinzu, der mir schließlich meine URL-Rewritefunktion aufruft:
void RewriteModule_BeginRequest(object sender, EventArgs e)

Nachdem ich meinen RewriteModuleSectionHandler instanziiert habe (über RewriteModuleSectionHandler cfg = (RewriteModuleSectionHandler)ConfigurationManager.GetSection("myModuleConfig/rewriteModule") findet die Instanziierung nur einmal statt), gehe ich alle Rewrite-Regeln durch und prüfe mit Regex und Match, ob der reguläre Ausdruck der Anfrage-URL mit dem der Regel übereinstimmt. Mit Regex.Replace(…) wird die URL endlich umgeschrieben.

Design Patterns – Proxy

Das Proxy-Pattern legt eine Zugriffskontrollschicht über ein Objekt (Subjekt). Da Originalobjekt wie Proxyobjekt das selbe Interface Subject implementieren, kann das Proxyobjekt als Stellvertreter (Proxy) verwendet werden.
So alltäglich der Zugriff auf Objekte für den Programmierer ist, so mannigfaltig können die Zugriffskontrollen des Proxy sein. Der Proxy kann etwa den Zugriff auf ein entferntes Objekt kapseln und kann für den Client unsichtbar das Verbindungsmanagement übernehmen (Remote Proxy). Ohne es zu wissen, habe ich diese Art Proxy verwendet, um den Aktienwerte zurück gebenden Webservice zu testen. Anhand der WSDL generiert mir Visual Studio eine Servicereferenz, die als Proxy dient.
Das Proxy kann auch zu dem Zweck existieren, das gekapselte Objekt erst bei Bedarf zu instanziieren (Virtual Proxy). Im GoF-Buch gibt es z.B. ein Dokument-Objekt, welches eine Vielzahl von primitiven Objekten wie Texte, geomentrische Elemente und Bilder enthält. Würde jedes Bildobjekt sofort instanziiert werden, könnte das sehr lange dauern und wäre in vielen Fällen unnötig.
Ein Schutzproxy erlaubt nur autorisierte Zugriffe auf das Objekt. Hier kann der Objektzugang benutzer- oder gruppenspezifisch geregelt werden.
Im Unterschied zum Decorator fügt der Proxy dem Subjekt keine Funktionalität hinzu. Es wird lediglich der Zugriff gesteuert.

Design Patterns – State

Das Zustand-Muster erlaubt es einem Objekt zur Laufzeit zustandsabhängig sein Verhalten zu ändern. Im Zeitalter der Objektorientierung programmiert man nach diesem Muster Zustandsautomaten. Statt für jede Zustandswechselmethode eine umfangreiche if-else-Orgie zu veranstalten, wird jeder Zustand in eine Klasse gekapselt. Zustände können so wesentlich leichter hinzugefügt werden, ohne jedesmal die if-else-Anweisungen erneut verstehen zu müssen. Selbst bei einem sehr komplexen Zustandsautomaten bleibt die Wartung übersichtlich.

<?php
abstract class WindowState
{
	public function OpenWide($window)
	{
	    echo "Keine Zustandsänderung<br/>";
	}
	public function Close($window)
	{
	    echo "Keine Zustandsänderung<br/>";
	}
	public function Tilt($window)
	{
	    echo "Keine Zustandsänderung<br/>";
	}
}

class WindowClosedState extends WindowState
{
	public function OpenWide(Window $window)
	{
	    echo "Fenster wurde geöffnet<br/>";
	    $window->currentState=new WindowWideOpenedState();
	}
	public function Tilt(Window $window)
	{
	    echo "Fenster wurde gekippt<br/>";
	    $window->currentState=new WindowTiltState();
	}
}

class WindowWideOpenedState extends WindowState
{
	public function Tilt(Window $window)
	{
	    echo "Fenster wurde gekippt<br/>";
	    $window->currentState=new WindowTiltState();
	}
	public function Close(Window $window)
	{
	    echo "Fenster wurde geschlossen<br/>";
	    $window->currentState=new WindowClosedState();
	}
}

class WindowTiltState extends WindowState
{
	public function Close(Window $window)
	{
	    echo "Fenster wurde geschlossen<br/>";
	    $window->currentState=new WindowClosedState();
	}
	public function Open(Window $window)
	{
	    echo "Fenster wurde geöffnet<br/>";
	    $window->currentState=new WindowOpenedState();
	}
}
class Window
{
	public $currentState;

	public function Window()
	{
		$this->currentState=new WindowClosedState();
	}

	public function OpenWide()
	{
		$this->currentState->OpenWide($this);
	}

	public function Close()
	{
		$this->currentState->Close($this);
	}

	public function Tilt()
	{
		$this->currentState->Tilt($this);
	}
}

$window = new Window();
$window->Close();

$window->OpenWide();
$window->Tilt();
$window->Tilt();
$window->OpenWide();
$window->Close();

?>